赞
踩
树 是一种经常用到的数据结构,用来模拟具有树状结构性质的数据集合。
树里的每一个节点有一个根植和一个包含所有子节点的列表。从图的观点来看,树也可视为一个拥有 N 个节点和 N-1 条边的一个有向无环图。
二叉树是一种更为典型的树树状结构。如它名字所描述的那样,二叉树是每个节点最多有两个子树的树结构,通常子树被称作“左子树”和“右子树”。
前序遍历首先访问根结点,然后遍历左子树,最后遍历右子树。
给定一个二叉树,返回它的前序遍历。
示例:
输入: [1,null,2,3]
1
\
2
/
3
输出: [1,2,3]
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ class Solution { public List<Integer> preorderTraversal(TreeNode root) { List<Integer> list = new ArrayList<>(); preorder(root, list); return list; } void preorder(TreeNode root, List<Integer> list) { if(root != null) { list.add(root.val); preorder(root.left, list); preorder(root.right, list); } } } class Solution { public List<Integer> preorderTraversal(TreeNode root) { List<Integer> list = new ArrayList<>(); if(root == null) return list; Stack<TreeNode> stack = new Stack<>(); stack.push(root); while(!stack.isEmpty()) { TreeNode curr = stack.pop(); list.add(curr.val); if(curr.right != null) stack.push(curr.right); if(curr.left != null) stack.push(curr.left); } return list; } }
中序遍历首先遍历左子树,然后访问根结点,最后遍历右子树。
给定一个二叉树,返回它的中序遍历。
示例:
输入: [1,null,2,3]
1
\
2
/
3
输出: [1,3,2]
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ class Solution { public List<Integer> inorderTraversal(TreeNode root) { List<Integer> res = new ArrayList<>(); if(root != null) { inorder(root, res); } return res; } void inorder(TreeNode root, List<Integer> res) { if(root.left != null) { inorder(root.left, res); } res.add(root.val); if(root.right != null) { inorder(root.right, res); } } } class Solution { public List<Integer> inorderTraversal(TreeNode root) { List<Integer> res = new ArrayList<>(); Stack<TreeNode> stack = new Stack<>(); TreeNode cur = root; while(cur != null || !stack.isEmpty()) { if(cur != null) { stack.push(cur); cur = cur.left; } else { cur = stack.pop(); res.add(cur.val); cur = cur.right; } } return res; } }
后序遍历首先遍历左子树,然后遍历右子树,最后访问树的根结点。
给定一个二叉树,返回它的后序遍历。
示例:
输入: [1,null,2,3]
1
\
2
/
3
输出: [3,2,1]
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ class Solution { public List<Integer> postorderTraversal(TreeNode root) { List<Integer> res = new ArrayList<>(); if(root != null) { postorder(root, res); } return res; } void postorder(TreeNode root, List<Integer> res) { if(root.left != null) { postorder(root.left, res); } if(root.right != null) { postorder(root.right, res); } res.add(root.val); } } class Solution { public List<Integer> postorderTraversal(TreeNode root) { List<Integer> res = new ArrayList<>(); if(root == null) { return res; } Stack<TreeNode> s = new Stack<>(); TreeNode cur = null, pre = null; s.push(root); while(!s.isEmpty()) { cur = s.peek(); if((cur.left == null && cur.right == null) || (pre != null && (pre == cur.left || pre == cur.right))) { res.add(cur.val); s.pop(); pre = cur; } else { if(cur.right != null) { s.push(cur.right); } if(cur.left != null) { s.push(cur.left); } } } return res; } }
层序遍历就是逐层遍历树结构。
广度优先搜索是一种广泛运用在树或图这类数据结构中,遍历或搜索的算法。 该算法从一个根节点开始,首先访问节点本身。 然后遍历它的相邻节点,其次遍历它的二级邻节点、三级邻节点,以此类推。
当我们在树中进行广度优先搜索时,我们访问的节点的顺序是按照层序遍历顺序的。
给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。
例如:
给定二叉树: [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回其层次遍历结果:
[
[3],
[9,20],
[15,7]
]
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ class Solution { List<List<Integer>> levels = new ArrayList<List<Integer>>(); public List<List<Integer>> levelOrder(TreeNode root) { if(root == null) { return levels; } order(root, 0); return levels; } void order(TreeNode node, int level) { if(levels.size() == level) { levels.add(new ArrayList<Integer>()); } levels.get(level).add(node.val); if(node.left != null) { order(node.left, level + 1); } if(node.right != null) { order(node.right, level + 1); } } }
递归是解决树的相关问题最有效和最常用的方法之一。
树可以以递归的方式定义为一个节点(根节点),它包括一个值和一个指向其他节点指针的列表。 递归是树的特性之一。 因此,许多树问题可以通过递归的方式来解决。 对于每个递归层级,我们只能关注单个节点内的问题,并通过递归调用函数来解决其子节点问题。
通常,我们可以通过 “自顶向下” 或 “自底向上” 的递归来解决树问题。
“自顶向下” 意味着在每个递归层级,我们将首先访问节点来计算一些值,并在递归调用时将这些值传递到子节点,所以 “自顶向下” 的解决方案可以被认为是一种前序遍历。
递归函数 top_down(root, params)
的原理:
1. return specific value for null node
2. update the answer if needed // anwer <-- params
3. left_ans = top_down(root.left, left_params) // left_params <-- root.val, params
4. right_ans = top_down(root.right, right_params) // right_params <-- root.val, params
5. return the answer if needed // answer <-- left_ans, right_ans
例如,思考这样一个问题:给定一个二叉树,请寻找它的最大深度。
我们知道根节点的深度是1
。 对于每个节点,如果我们知道某节点的深度,那我们将知道它子节点的深度。 因此,在调用递归函数的时候,将节点的深度传递为一个参数,那么所有的节点都知道它们自身的深度。 而对于叶节点,我们可以通过更新深度从而获取最终答案。 这里是递归函数 maximum_depth(root, depth
的伪代码:
1. return if root is null
2. if root is a leaf node:
3. answer = max(answer, depth) // update the answer if needed
4. maximum_depth(root.left, depth + 1) // call the function recursively for left child
5. maximum_depth(root.right, depth + 1) // call the function recursively for right child
int answer; // don't forget to initialize answer before call maximum_depth
void maximum_depth(TreeNode* root, int depth) {
if (!root) {
return;
}
if (!root->left && !root->right) {
answer = max(answer, depth);
}
maximum_depth(root->left, depth + 1);
maximum_depth(root->right, depth + 1);
}
private int answer; // don't forget to initialize answer before call maximum_depth
private void maximum_depth(TreeNode root, int depth) {
if (root == null) {
return;
}
if (root.left == null && root.right == null) {
answer = Math.max(answer, depth);
}
maximum_depth(root.left, depth + 1);
maximum_depth(root.right, depth + 1);
}
“自底向上” 是另一种递归方法。 在每个递归层次上,我们首先对所有子节点递归地调用函数,然后根据返回值和根节点本身的值得到答案。 这个过程可以看作是后序遍历的一种。
递归函数 bottom_up(root)
的原理:
1. return specific value for null node
2. left_ans = bottom_up(root.left) // call function recursively for left child
3. right_ans = bottom_up(root.right) // call function recursively for right child
4. return answers // answer <-- left_ans, right_ans, root.val
继续思考上面的问题:给定一个二叉树,请寻找它的最大深度。但是使用不同的思维方式:对于树的单个节点,以节点自身为根的子树的最大深度x
是多少?
如果我们知道一个根节点,以其左子节点为根的最大深度为l
和以其右子节点为根的最大深度为r
,我们是否可以回答前面的问题? 当然可以,我们可以选择它们之间的最大值,再加上1来获得根节点所在的子树的最大深度。 那就是 x = max(l,r)+ 1
。
这意味着对于每一个节点来说,我们都可以在解决它子节点的问题之后得到答案。 因此,我们可以使用“自底向上“的方法。
下面是递归函数 maximum_depth(root)
的伪代码:
1. return 0 if root is null // return 0 for null node
2. left_depth = maximum_depth(root.left)
3. right_depth = maximum_depth(root.right)
4. return max(left_depth, right_depth) + 1 // return depth of the subtree rooted at root
int maximum_depth(TreeNode* root) {
if (!root) {
return 0; // return 0 for null node
}
int left_depth = maximum_depth(root->left);
int right_depth = maximum_depth(root->right);
return max(left_depth, right_depth) + 1; // return depth of the subtree rooted at root
}
public int maximum_depth(TreeNode root) {
if (root == null) {
return 0; // return 0 for null node
}
int left_depth = maximum_depth(root.left);
int right_depth = maximum_depth(root.right);
return Math.max(left_depth, right_depth) + 1; // return depth of the subtree rooted at root
}
当遇到树问题时,请先思考一下两个问题:
如果答案都是肯定的,那么请尝试使用 “自顶向下” 的递归来解决此问题。
或者你可以这样思考:对于树中的任意一个节点,如果你知道它子节点的答案,你能计算出该节点的答案吗? 如果答案是肯定的,那么 “自底向上” 的递归可能是一个不错的解决方法。
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回它的最大深度 3 。
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ /** * 自底向上 */ class Solution { public int maxDepth(TreeNode root) { if(root == null) { return 0; } int leftDepth = maxDepth(root.left); int rightDepth = maxDepth(root.right); return Math.max(leftDepth, rightDepth) + 1; } } /** * 自顶向下 */ class Solution { private int ans = 0; public int maxDepth(TreeNode root) { maximum_depth(root, 1); return ans; } void maximum_depth(TreeNode node, int depth) { if(node == null) { return; } if(node.left == null && node.right == null) { ans = Math.max(ans, depth); } maximum_depth(node.left, depth + 1); maximum_depth(node.right, depth + 1); } }
给定一个二叉树,检查它是否是镜像对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
1
/ \
2 2
/ \ / \
3 4 4 3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
1
/ \
2 2
\ \
3 3
说明:
如果你可以运用递归和迭代两种方法解决这个问题,会很加分。
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ class Solution { public boolean isSymmetric(TreeNode root) { if(root == null) { return true; } return IsSubSymmetric(root.left, root.right); } boolean IsSubSymmetric(TreeNode left, TreeNode right) { if(left == null && right == null) { return true; } if(left == null || right == null) { return false; } return (left.val == right.val) && IsSubSymmetric(left.left, right.right) && IsSubSymmetric(left.right, right.left); } }
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ class Solution { public boolean isSymmetric(TreeNode root) { Queue<TreeNode> q = new LinkedList<TreeNode>(); if(root == null) { return true; } q.add(root.left); q.add(root.right); while(q.size() > 1) { TreeNode left = q.poll(); TreeNode right = q.poll(); if(left == null && right == null) { continue; } if(left == null ^ right == null) { return false; } if(left.val != right.val) { return false; } q.add(left.left); q.add(right.right); q.add(left.right); q.add(right.left); } return true; } }
给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
说明: 叶子节点是指没有子节点的节点。
示例:
给定如下二叉树,以及目标和 sum = 22
,
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
返回 true
, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2
。
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ class Solution { public boolean hasPathSum(TreeNode root, int sum) { if(root == null) { return false; } if(root.left == null && root.right == null) { return sum == root.val; } sum -= root.val; return hasPathSum(root.left, sum) || hasPathSum(root.right, sum); } }
根据一棵树的中序遍历与后序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
例如,给出
中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ class Solution { public TreeNode buildTree(int[] inorder, int[] postorder) { if(postorder.length == 0) { return null; } int rootValue = postorder[postorder.length - 1]; TreeNode root = new TreeNode(rootValue); int leftSize = find(inorder, rootValue); int[] leftInorder = Arrays.copyOfRange(inorder, 0, leftSize); int[] leftPostorder = Arrays.copyOfRange(postorder, 0, leftSize); root.left = buildTree(leftInorder, leftPostorder); int[] rightInorder = Arrays.copyOfRange(inorder, leftSize + 1, inorder.length); int[] rightPostorder = Arrays.copyOfRange(postorder, leftSize, inorder.length - 1); root.right = buildTree(rightInorder, rightPostorder); return root; } private int find(int[] array, int v) { for (int i = 0; i < array.length; i++) { if (array[i] == v) { return i; } } return -1; } }
根据一棵树的前序遍历与中序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ class Solution { public TreeNode buildTree(int[] preorder, int[] inorder) { if(preorder.length == 0) { return null; } int rootValue = preorder[0]; TreeNode root = new TreeNode(rootValue); int leftSize = find(inorder, rootValue); int[] leftInorder = Arrays.copyOfRange(inorder, 0, leftSize); int[] leftPreorder = Arrays.copyOfRange(preorder, 1, leftSize + 1); root.left = buildTree(leftPreorder, leftInorder); int[] rightInorder = Arrays.copyOfRange(inorder, leftSize + 1, inorder.length); int[] rightPreorder = Arrays.copyOfRange(preorder, leftSize + 1, preorder.length); root.right = buildTree(rightPreorder, rightInorder); return root; } private int find(int[] array, int v) { for (int i = 0; i < array.length; i++) { if (array[i] == v) { return i; } } return -1; } }
给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
示例:
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。
提示:
/* // Definition for a Node. class Node { public int val; public Node left; public Node right; public Node next; public Node() {} public Node(int _val,Node _left,Node _right,Node _next) { val = _val; left = _left; right = _right; next = _next; } }; */ class Solution { public Node connect(Node root) { if(root == null) { return null; } if(root.left != null) { root.left.next = root.right; } if(root.right != null) { root.right.next = root.next != null ? root.next.left : null; } connect(root.left); connect(root.right); return root; } }
给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
示例:
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。
提示:
/* // Definition for a Node. class Node { public int val; public Node left; public Node right; public Node next; public Node() {} public Node(int _val,Node _left,Node _right,Node _next) { val = _val; left = _left; right = _right; next = _next; } }; */ class Solution { public Node connect(Node root) { if(root == null) { return null; } Node p = root.next; while (p != null) { if (p.left != null) { p = p.left; break; } if (p.right != null) { p = p.right; break; } p = p.next; } if(root.right != null) { root.right.next = p; } if(root.left != null) { if(root.right != null) { root.left.next = root.right; } else { root.left.next = p; } } connect(root.right); connect(root.left); return root; } }
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]
示例 1:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
示例 2:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。
说明:
解题思路:
递归搜索左右子树,如果左子树和右子树都不为空,说明最近父节点一定在根节点。
反之,如果左子树为空,说明两个节点一定在右子树;
同理如果右子树为空,说明两个节点一定在左子树
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ class Solution { public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { if(root == null) { return null; } if(root == p || root == q) { return root; } TreeNode left = lowestCommonAncestor(root.left, p, q); TreeNode right = lowestCommonAncestor(root.right, p, q); if(left != null && right != null) { return root; } if(left == null) { return right; } if(right == null) { return left; } return null; } }
序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。
请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。
示例:
你可以将以下二叉树:
1
/ \
2 3
/ \
4 5
序列化为 “[1,2,3,null,null,4,5]”
提示:
说明:
思路:
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ public class Codec { public String rserialize(TreeNode root, String str) { // Recursive serialization. if (root == null) { str += "null,"; } else { str += str.valueOf(root.val) + ","; str = rserialize(root.left, str); str = rserialize(root.right, str); } return str; } // Encodes a tree to a single string. public String serialize(TreeNode root) { return rserialize(root, ""); } public TreeNode rdeserialize(List<String> l) { // Recursive deserialization. if (l.get(0).equals("null")) { l.remove(0); return null; } TreeNode root = new TreeNode(Integer.valueOf(l.get(0))); l.remove(0); root.left = rdeserialize(l); root.right = rdeserialize(l); return root; } // Decodes your encoded data to tree. public TreeNode deserialize(String data) { String[] data_array = data.split(","); List<String> data_list = new LinkedList<String>(Arrays.asList(data_array)); return rdeserialize(data_list); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。