二叉树题目合集
104. Maximum Depth of Binary Tree
找到二叉树的最大深度。
public class Solution {
public int maxDepth(TreeNode root) {
if (root == null) {
return 0;
}
int l = maxDepth(root.left) + 1;
int r = maxDepth(root.right) + 1;
return l > r ? l : r;
}
}
111. Minimum Depth of Binary Tree
找到二叉树的最小深度
思路:与最大深度不同的地方在于,若一个节点只有左节点或者右节点的其中之一的节点,则该节点并不为完整路径的叶节点,一定要找到叶节点才能判断最小路径
public class Solution {
public int minDepth(TreeNode root) {
if (root == null) {
return 0;
}
if (root.left == null) {
return minDepth(root.right) + 1;
}
if (root.right == null) {
return minDepth(root.left) + 1;
}
return Math.min(minDepth(root.left), minDepth(root.right)) + 1;
}
}
112. Path Sum
给定一个树和一个整数,决定是否该树有从跟节点到叶节点的一条路径上的值的和等于给定的这个整数
思路:前序遍历,若没有左右子节点的话,判断当前根节点是否等于sum,若不等于,sum减去当前根节点的值,继续向下遍历。
public class Solution {
public boolean hasPathSum(TreeNode root, int sum) {
if (root == null) {
return false;
}
if (root.left == null && root.right == null && sum == root.val) {
return true;
}
return (hasPathSum(root.left, sum-root.val) || hasPathSum(root.right, sum-root.val));
}
}
113. Path Sum II
给定一个树和一个整数,找到所有从根节点到叶节点的路径上的值的和等于给的这个整数,要返回下面这种。
[ [5,4,11,2], [5,8,4,5] ]
思路:与上一题不同之处在于,上一题是判断存不存在,该题要找到所有符合的路径,两个list是必不可少。内层list来存储符合的路径。然后遍历到叶节点符合条件,就将对应的一条路径添加到外层list中,这里有两个关键点,前序遍历是必不可少,问题是遍历的过程中如果访问到某一叶节点,但这条路径不符合。那么内层list需要一个一个remove掉当前元素,即使该路径符合,为了寻找新的符合条件路径,也需要remove掉当前的值。所以在遍历的最后要remove掉当前节点的值。
易错的地方:因为innerlist是处于一直增删的状态。所以确定了某一完整的符合条件的路径后,应新建一个temp的list来存储对应的innerlist,但不能直接把引用=innerlist,这样两者的地址会一样。所以方法是将innerlist的值复制到temp中,所以应该用 addAll方法来处理。
public class Solution {
List<List<Integer>> list = new ArrayList<>();
List<Integer> innerlist = new ArrayList<>();
public List<List<Integer>> pathSum(TreeNode root, int sum) {
if (root == null) {
return list;
}
innerlist.add(root.val);
hasPathSum(root, sum - root.val);
return list;
}
public void hasPathSum(TreeNode root, int sum) {
if (root.left == null && root.right == null && sum == 0) {
List<Integer> temp = new ArrayList<>();
temp.addAll(innerlist);
list.add(temp);
}
if (root.left != null) {
innerlist.add(root.left.val);
hasPathSum(root.left, sum - root.left.val);
innerlist.remove(innerlist.size() - 1);
}
if (root.right != null) {
innerlist.add(root.right.val);
hasPathSum(root.right, sum - root.right.val);
innerlist.remove(innerlist.size() - 1);
}
}
}
437. Path Sum III
给定一个树和一个整数,找到所有从任意节点到任意节点的路径上的值的和等于给的这个整数,但一定是从上到下的逻辑顺序。返回共有多少条路径。
思路:与前两题不一样的地方在于 不是从根节点开始了,而是从任意节点开始到下面节点路径和为该值即可。很典型的动态规划的问题。先考虑跟节点去往下遍历,记录从根节点开始的符合情况,接着从根节点的左右子节点开始遍历。
public class Solution {
public int pathSum(TreeNode root, int sum) {
if (root == null) {
return 0;
}
return dfs(root, sum) + pathSum(root.left, sum) + pathSum(root.right, sum);
}
public int dfs(TreeNode root, int sum) {
int res = 0;
if (root == null) {
return 0;
}
if (root.val == sum) {
++res;
}
res += dfs(root.left, sum - root.val);
res += dfs(root.right, sum - root.val);
return res;
}
}
102. Binary Tree Level Order Traversal 待修改,减少空间复杂度
给定一个二叉树,从左到右,一层一层返回遍历的值。类似下面这种
3 / \ 9 20 / \ 15 7
return its level order traversal as:
[ [3], [9,20], [15,7] ]
思路:利用两个双端队列linkedlist去存储当前遍历的层以及下一层要遍历的元素,通过remove将队列中的节点一个一个处理。
public class Solution { public List<List<Integer>> levelOrder(TreeNode root) { List<List<Integer>> list = new ArrayList<List<Integer>>(); ArrayList<Integer> nodeValue = new ArrayList<Integer>(); if (root == null){ return list; } LinkedList<TreeNode> current = new LinkedList<TreeNode>(); LinkedList<TreeNode> next = new LinkedList<TreeNode>(); current.add(root); while (!current.isEmpty()) { TreeNode node = current.remove(); if (node.left != null) { next.add(node.left); } if (node.right != null) { next.add(node.right); } nodeValue.add(node.val); if (current.isEmpty()) { current = next; next = new LinkedList<TreeNode>(); list.add(nodeValue); nodeValue = new ArrayList<Integer>(); } } return list; } }
107. Binary Tree Level Order Traversal II 待修改,减少空间复杂度
与上题不同之处在于要倒序输出,返回如下这种
[ [15,7], [9,20], [3] ]
思路:最后对上一题的list进行reverse即可。
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ public class Solution { public List<List<Integer>> levelOrderBottom(TreeNode root) { List<List<Integer>> list = new ArrayList<List<Integer>>(); List<Integer> nodeValue = new ArrayList<Integer>(); if (root == null){ return list; } LinkedList<TreeNode> current = new LinkedList<TreeNode>(); LinkedList<TreeNode> next = new LinkedList<TreeNode>(); current.add(root); while (!current.isEmpty()){ TreeNode node = current.remove(); if (node.left != null){ next.add(node.left); } if (node.right != null){ next.add(node.right); } nodeValue.add(node.val); if (current.isEmpty()){ current = next; next = new LinkedList<TreeNode>(); list.add(nodeValue); nodeValue = new ArrayList<Integer>(); } } Collections.reverse(list);return reverseList; } }
103. Binary Tree Zigzag Level Order Traversal
给定一个二叉树,返回它的zigzag型的遍历形式
For example:
Given binary tree [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
return its zigzag level order traversal as:
[
[3],
[20,9],
[15,7]
]
思路:利用linkedlist是一个双端队列,也可以当栈来使用的特性输出。
public class Zigzag_Traversal { public List<List<Integer>> zigzagLevelOrder(TreeNode root) { List<List<Integer>> list = new ArrayList<>(); List<Integer> nodeValues = new ArrayList<>(); if (root == null){ return list; } int i = 0; LinkedList<TreeNode> current = new LinkedList<>(); LinkedList<TreeNode> next = new LinkedList<>(); current.add(root); while (!current.isEmpty()) { TreeNode node = current.removeLast(); nodeValues.add(node.val); if (i % 2 == 0){ if (node.left != null){ next.add(node.left); } if (node.right != null){ next.add(node.right); } } if (i % 2 == 1){ if (node.right != null){ next.add(node.right); } if (node.left != null){ next.add(node.left); } } if (current.isEmpty()){ i++; list.add(nodeValues); current = next; next = new LinkedList<TreeNode>(); nodeValues = new ArrayList<Integer>(); } } return list; } }
100. Same Tree
给两个二叉树,判断这个两个二叉树是不是相同的,如果他们结构一致,且节点值相同,则为相同
思路:该题不难,遍历对应的节点值是否相等即可。
public class Solution { public boolean isSameTree(TreeNode p, TreeNode q) { if (p == null && q == null) { return true; } if (p == null || q == null) { return false; } if (p.val != q.val) { return false; } return isSameTree(p.left, q.left) && isSameTree(p.right, q.right); } }
101. Symmetric Tree
给定一个树,判断该树是不是对称的结构
For example, this binary tree [1,2,2,3,4,4,3]
is symmetric:
1 / \ 2 2 / \ / \ 3 4 4 3
But the following [1,2,2,null,3,null,3]
is not:
1 / \ 2 2 \ \ 3 3
思路:对称一定是在同一层中,最左边节点等于最右边节点,其次第二个值等于倒数第二个值,依次到中间。
易错点,容易单纯判断某一节点的左右子节点是否相等。这并不是对陈数的定义。所以应该判断左子节点的左子节点与右子节点的右子节点是否相等,以及左子节点的右子节点与右子节点的左子节点是否相等。
public class Solution { public boolean isSymmetric(TreeNode root) { if (root == null) { return true; } return isSymmetric(root, root); } public boolean isSymmetric(TreeNode p, TreeNode q) { if (p == null && q == null) { return true; } if (p == null || q == null) { return false; } if (p.val != q.val) { return false; } return isSymmetric(p.left, q.right) && isSymmetric(p.right, q.left); } }
110. Balanced Binary Tree
给定一个树,判断该树是否高度平衡,高度平衡指的是某节点的两个子树高度差不能大于1
思路:递归遍历整个树,判断每个节点的左子节点的高度与右子节点的高度差是否大于1即可。
public class Solution { public boolean isBalanced(TreeNode root) { if (root == null) { return true; } int l = depth(root.left); int r = depth(root.right); if (Math.abs(l - r) > 1) { return false; } return isBalanced(root.left) && isBalanced(root.right); } public int depth(TreeNode root) { if (root == null) { return 0; } int l = depth(root.left) + 1; int r = depth(root.right) + 1; return l > r ? l:r; } }
226. Invert Binary Tree
翻转一个二叉树,类似如下这种
4 / \ 2 7 / \ / \ 1 3 6 9
to
4 / \ 7 2 / \ / \ 9 6 3 1
思路:该题不难,可以先翻转左右子节点,然后再翻转每个子节点的左右子节点。递归处理。
public class Solution { public TreeNode invertTree(TreeNode root) { if (root == null) { return null; } TreeNode node = root.left; root.left = root.right; root.right = node; inverTree(root.left); inverTree(root.right); return root; } }
257. Binary Tree Paths
返回他的从根到叶的所有路径如下:
1
/ \
2 3
\
5
All root-to-leaf paths are:
["1->2->5", "1->3"]
思路:该题不难,递归去访问是否存在子节点即可。
public class Solution { List<String> list = new ArrayList<String>(); public List<String> binaryTreePaths(TreeNode root) { if (root == null) { return list; } path(root, String.valueOf(root.val)); return list; } public void path(TreeNode root, String paths) { if (root.left == null && root.right == null) { list.add(paths); } if (root.left != null) { path(root.left, paths + "->" + root.left.val); } if (root.right != null) { path(root.right, paths + "->" + root.right.val); } } }
235. Lowest Common Ancestor of a Binary Search Tree
给定一个二叉搜索树,找到在这个树中任意两个节点的公共父节点
_______6______
/ \
___2__ ___8__
/ \ / \
0 _4 7 9
/ \
3 5
For example, the lowest common ancestor (LCA) of nodes 2 and 8 is 6. Another example is LCA of nodes 2 and 4 is 2
思路:鉴于这是一个二叉搜索树,那么给定的两个节点若一个大于根节点,一个小于根节点,那么根节点即为她们的公共父节点,若两个节点都小于根节点,则在左子节点方向遍历。反之,在右子节点方向遍历。
若不是二叉搜索树呢?这个时候就不能单纯凭借以上条件来判断,我们分别遍历树,记录找到对应的这两个节点的路径,通过比较两个节点的路径,最后一个相同的节点路径即为公共父节点,
第一种解法(适用于bst)
public class Solution { public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q){ if (root == null || p == null || q == null){ return null; } if (p.val < root.val && q.val < root.val) { return lowestCommonAncestor(root.left, p, q); } if (p.val > root.val && q.val > root.val) { return lowestCommonAncestor(root.right, p, q); } else { return root; } } }
第二种解法(通用)
public class Solution { public TreeNode lowestCommonAncestor(TreeNode root,TreeNode p,TreeNode q){ if (root == null || p == null || q == null){ return null; } List<TreeNode> pathp = new ArrayList<>(); List<TreeNode> pathq = new ArrayList<>(); pathp.add(root); pathq.add(root); getPath(root, p, pathp); getPath(root, q, pathq); TreeNode lca = null; for (int i = 0;i < pathq.size() && i < pathp.size();i++){ if (pathp.get(i) == pathq.get(i)){ lca = pathp.get(i); } } return lca; } public boolean getPath(TreeNode root, TreeNode n, List<TreeNode> path){ if (root == n){ return true; } if (root.left != null){ path.add(root.left); if(getPath(root.left,n,path)) return true; path.remove(path.size() - 1); } if (root.right != null){ path.add(root.right); if (getPath(root.right, n, path)) return true; path.remove(path.size() - 1); } return false; } }
404. Sum of Left Leaves
找到该树中所有左子树叶子的和
3
/ \
9 20
/ \
15 7 Return 24.
思路:该题不难就是遍历寻找,找到左子节点后,判断左子节点是否是叶节点即可。
public class Solution { int sum=0; public int sumOfLeftLeaves(TreeNode root) { if(root== null) { return 0; } if(root.left!=null) { if(root.left.left==null&&root.left.right==null) { sum+=root.left.val; } } sumOfLeftLeaves(root.left); sumOfLeftLeaves(root.right); return sum; } }
270:Closest Binary Search Tree Value
给定一个非空的二叉搜索树,和一个目标值(浮点型),找到该树中最接近该值的那个节点的值。该树中只有一个满足条件的值。
思路:从根节点遍历,找到与该目标值的差值保存,若目标值小于当前根节点的值,则向左遍历,反之向右遍历
public class Solution { public int closetValue(TreeNode root, double target){ double min = Double.MAX_VALUE; int closet = root.val; while (root != null){ if (Math.abs(root.val - target) < min){ min = Math.abs(root.val - target); closet = root.val; } if (target < root.val){ root = root.left; } if (target > root.val){ root = root.right; } else { return root.val; } } return closet; } }
236. Lowest Common Ancestor of a Binary Tree
给定一个二叉树,和两个节点,找到这两个节点的父节点。
思路:类似235题。不同在于这个就不是二叉搜索树了。所以找路径即可。(注意两个地方:不是bst的话 数值有可能会重复。因此判断的时候要用tree类型判断,而不能用val判断,另外搜寻某个node的时候,利用if+递归来判断是否找到)
public class Solution { public TreeNode lowestCommonAncestor(TreeNode root,TreeNode p,TreeNode q){ if (root == null || p == null || q == null){ return null; } List<TreeNode> pathp = new ArrayList<>(); List<TreeNode> pathq = new ArrayList<>(); pathp.add(root); pathq.add(root); getPath(root, p, pathp); getPath(root, q, pathq); TreeNode lca = null; for (int i = 0;i < pathq.size() && i < pathp.size();i++){ if (pathp.get(i) == pathq.get(i)){ lca = pathp.get(i); } } return lca; } public boolean getPath(TreeNode root, TreeNode n, List<TreeNode> path){ if (root == n){ return true; } if (root.left != null){ path.add(root.left); if(getPath(root.left,n,path)) return true; path.remove(path.size() - 1); } if (root.right != null){ path.add(root.right); if (getPath(root.right, n, path)) return true; path.remove(path.size() - 1); } return false; } }
230. Kth Smallest Element in a BST
给定一个二叉搜索树,找到其中第k个最小的元素。 1<=k<=tree的大小
思路:两种解法:本质都是中序遍历,第一种是dfs:
public class Solution { int count = 1; int result = 0; public int kthSmallest(TreeNode root, int k) { if (root == null) { return 0; } dfs(root,k); return result; } public void dfs(TreeNode root, int k) { if (root == null) { return; } dfs(root.left,k); if (count == k) { result = root.val; } count++; dfs(root.right,k); } }
第二种解法:利用栈的中序遍历。
public class Solution { public int kthSmallest(TreeNode root, int k) { TreeNode n = root; Stack<TreeNode> stack = new Stack<>(); int result = 0; while (n != null || !stack.isEmpty()) { if (n != null){ stack.push(n); n = n.left; } else { TreeNode t = stack.pop(); k--; if (k == 0) { result = t.val; } n = t.right; } } return result; } }
105. Construct Binary Tree from Preorder and Inorder Traversal
给一个先序遍历和中序遍历的数组,构建对应的二叉树。
思路:
假设树的先序遍历是12453687,中序遍历是42516837。
这里最重要的一点就是先序遍历可以提供根的所在,而根据中序遍历的性质知道根的所在就可以将序列分为左右子树。
比如上述例子,我们知道1是根,所以根据中序遍历的结果425是左子树,而6837就是右子树。
接下来根据切出来的左右子树的长度又可以在先序便利中确定左右子树对应的子序列(先序遍历也是先左子树后右子树)。
根据这个流程,左子树的先序遍历和中序遍历分别是245和425,右子树的先序遍历和中序遍历则是3687和6837,我们重复以上方法,可以继续找到根和左右子树,直到剩下一个元素。
可以看出这是一个比较明显的递归过程,对于寻找根所对应的下标,我们可以先建立一个HashMap,以免后面需要进行线行搜索,这样每次递归中就只需要常量操作就可以完成对根的确定和左右子树的分割。
public class Solution { public TreeNode buildTree(int[] preorder, int[] inorder) { if (preorder == null || inorder == null) { return null; } HashMap<Integer, Integer> map = new HashMap<>(); for (int i = 0; i < inorder.length; i++) { map.put(inorder[i], i); } return help(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1, map); } public TreeNode help(int[] preorder, int startPre, int endPre, int[] inorder, int startIn, int endIn, HashMap<Integer, Integer> map) { if (startPre > endPre || startIn > endIn) { return null; } TreeNode root = new TreeNode(preorder[startPre]); int index = map.get(root.val); root.left = help(preorder, startPre + 1, startPre + index - startIn, inorder, startIn, index - 1, map); root.right = help(preorder, startPre + index - startIn + 1, endPre, inorder, index + 1, endIn, map); return root; } }
106. Construct Binary Tree from Inorder and Postorder Traversal
思路:这次是提供了中序和后续的遍历序列,同理,只不过根在最后面。
public class Solution { public TreeNode buildTree(int[] inorder, int[] postorder) { if (postorder == null || inorder == null){ return null; } HashMap<Integer,Integer>map = new HashMap<>(); for (int i = 0; i < inorder.length; i++){ map.put(inorder[i], i); } return help(postorder, 0,postorder.length-1,inorder,0,inorder.length-1,map); } public TreeNode help(int[]postorder,int startPost,int endPost,int[]inorder,int startIn,int endIn,HashMap<Integer,Integer>map){ if(startIn>endIn||startPost>endPost){ return null; } TreeNode root=new TreeNode(postorder[endPost]); int index=map.get(root.val); root.left=help(postorder,startPost,startPost+index-startIn-1,inorder,startIn,index-1,map); root.right=help(postorder,endPost-(endIn-index),endPost-1,inorder,index+1,endIn,map); return root; } }
94/144/145. Binary Tree Preorder/Inorder/Postorder Traversal
如果用迭代解决如下
pre
public List<Integer> preorderTraversal(TreeNode root) { List<Integer> result = new ArrayList<>(); Deque<TreeNode> stack = new ArrayDeque<>(); TreeNode p = root; while(!stack.isEmpty() || p != null) { if(p != null) { stack.push(p); result.add(p.val); // Add before going to children p = p.left; } else { TreeNode node = stack.pop(); p = node.right; } } return result; }
Inorder:
public List<Integer> inorderTraversal(TreeNode root) { List<Integer> result = new ArrayList<>(); Deque<TreeNode> stack = new ArrayDeque<>(); TreeNode p = root; while(!stack.isEmpty() || p != null) { if(p != null) { stack.push(p); p = p.left; } else { TreeNode node = stack.pop(); result.add(node.val); // Add after all left children p = node.right; } } return result; }
Postorder:
public List<Integer> postorderTraversal(TreeNode root) { LinkedList<Integer> result = new LinkedList<>(); Deque<TreeNode> stack = new ArrayDeque<>(); TreeNode p = root; while(!stack.isEmpty() || p != null) { if(p != null) { stack.push(p); result.addFirst(p.val); // Reverse the process of preorder p = p.right; // Reverse the process of preorder } else { TreeNode node = stack.pop(); p = node.left; // Reverse the process of preorder } } return result; }
222. Count Complete Tree Nodes
计算一个完全树的所有节点数量。
思路:先计算左右的深度是否相等(注意 这里有个易错点,不是判断左右子树的最大深度,而是判断该树的最左叶节点的深度是否和该树最右的叶节点是否相等,所以如果用最大深度去求就会出问题,应该用循环分别计算到最左叶节点和最右节点的长度)相等则为满二叉树,满二叉树的节点个数为深度的平方减一,即depth^2-1;
如果不相等,则递归以同样的方式计算左子树和右子树,并返回两者个数之和加一。
注意 用pow方法的时候很有可能超时,要学会利用位运算 <<
public class Solution { public int countNodes(TreeNode root) { if (root == null) { return 0; } int l = leftDepth(root) + 1; int r = rightDepth(root) + 1; if (l == r) { return (1 << l) - 1; } else { return countNodes(root.left) + countNodes(root.right) + 1; } } public int leftDepth(TreeNode root) { int count = 0; while (root.left != null){ root = root.left; count++; } return count; } public int rightDepth(TreeNode root) { int count = 0; while (root.right != null){ root = root.right; count++; } return count; } }
199. Binary Tree Right Side View
给一个二叉树,返回它从右侧看显示的数值
For example:
Given the following binary tree,
1 <--- / \ 2 3 <--- \ \ 5 4 <---
You should return [1, 3, 4]
.
思路:传统的层次遍历,返回最右侧的值,但此处用个更简单的方法,因为每层只添加一个元素,那么二叉树的深度和list中的元素是对应增长的。先遍历右子节点,存在,添加进list,再次判断list的数量和深度的关系,不相等说明已经添加过了该层的元素,继续向下遍历。
public class Solution { public List<Integer> rightSideView(TreeNode root) { List<Integer> list = new ArrayList<>(); if (root == null) { return list; } help(root, list, 0); return list; } public void help(TreeNode root, List<Integer> list, int depth) { if (root == null) { return; } if (depth == list.size()) { list.add(root.val); } help(root.right, list, depth + 1); help(root.left, list, depth + 1); } }
173. Binary Search Tree Iterator
Implement an iterator over a binary search tree (BST). Your iterator will be initialized with the root node of a BST.
Calling next()
will return the next smallest number in the BST.
Note: next()
and hasNext()
should run in average O(1) time and uses O(h) memory, where h is the height of the tree.
思路:该题每次调用next要返还下一个最小的值。用栈去处理。中序遍历。
public class BSTIterator { TreeNode node = null; Stack<TreeNode> stack = new Stack<>(); public BSTIterator(TreeNode root) { node = root; } /** @return whether we have a next smallest number */ public boolean hasNext() { return !(stack.isEmpty() && node == null); } /** @return the next smallest number */ public int next() { TreeNode res = null; if (node == null) { res = stack.pop(); node = res.right; } else { while(node.left != null) { stack.push(node); node = node.left; } res = node; node = node.right; } return res.val; } }
leetcode 156: Binary Tree Upside Down
Given a binary tree where all the right nodes are either leaf nodes with a sibling (a left node that shares the same parent node) or empty, flip it upside down and turn it into a tree where the original right nodes turned into left leaf nodes. Return the new root.
For example:
Given a binary tree {1,2,3,4,5},
1
/ \
2 3
/ \
4 5
return the root of the binary tree [4,5,2,#,#,3,1].
4
/ \
5 2
/ \
3 1
思路:这道题让我们把一棵二叉树上下颠倒一下,而且限制了右节点要么为空要么一定会有对应的左节点。上下颠倒后原来二叉树的最左子节点变成了根节点,其对应的右节点变成了其左子节点,其父节点变成了其右子节点,相当于顺时针旋转了一下。对于一个根节点来说,我们的目标是将其左子节点变为根节点,右子节点变为左子节点,原根节点变为右子节点,那么我们首先判断这个根节点是否存在,且其有没有左子节点,如果不满足这两个条件的话,直接返回即可,不需要翻转操作。那么我们不停的对左子节点调用递归函数,直到到达最左子节点开始翻转,翻转好最左子节点后,开始回到上一个左子节点继续翻转即可,直至翻转完整棵树
public class UpsideDown { public TreeNode UpsideDownBinaryTree(TreeNode root) { if (root == null) { return null; } TreeNode parent = root; TreeNode left = root.left; TreeNode right = root.right; if (left != null) { TreeNode temp = UpsideDownBinaryTree(left); left.left=right; left.right=parent; return temp; } return root; } }
366. Find Leaves of Binary Tree
Given a binary tree, find all leaves and then remove those leaves. Then repeat the previous steps until the tree is empty.
Example:
Given binary tree
1
/ \
2 3
/ \
4 5
Returns [4, 5, 3], [2], [1].
思路:一种比较直接的思路是每次截取叶子结点保存起来, 然后再从根开始遍历剪去叶子结点. 但是这样重复遍历太多, 其时间复杂度最坏可以达到O(n!)。换个思维考虑,考虑每个节点的最大深度,每个节点的最大深度其实就是内层list的对应位置。因此可以在遍历的同时判断该节点的深度,同时将对应节点的值添加到对应的list位置。
public class Solution { public List<List<Integer>> findLeaves(TreeNode root) { List<List<Integer>> list = new ArrayList<>(); if (root == null) { return list; } for(int i = 0; i < depth(root); i++) { list.add(new ArrayList<>()); } depth(root,list); return list; } public int depth(TreeNode root) { if(root == null) { return 0; } int l=depth(root.left); int r=depth(root.right); return l>r? l+1:r+1; } public int depth(TreeNode root, List<List<Integer>> list) { if(root==null) { return 0; } int depth=1; int left=depth(root.left,list); int right=depth(root.right,list); depth+=Math.max(left,right); list.get(depth-1).add(root.val); return depth; } }
129. Sum Root to Leaf Numbers
Given a binary tree containing digits from 0-9
only, each root-to-leaf path could represent a number.
An example is the root-to-leaf path 1->2->3
which represents the number 123
.
Find the total sum of all root-to-leaf numbers.
For example,
1 / \ 2 3
The root-to-leaf path 1->2
represents the number 12
.
The root-to-leaf path 1->3
represents the number 13
.
Return the sum = 12 + 13 = 25
.
思路:两种第一种用string存储完整路径,然后将string转换为int。第二种更简单,每遍历一次,将对应的sum*10再加上当前的val
public class Solution { public int sumNumbers(TreeNode root) { return help(root, 0); } public int help(TreeNode root, int sum) { if (root == null) { return 0; } if (root.left == null && root.right == null) { return sum * 10 + root.val; } return help(root.left, sum * 10 + root.val) + help(root.right, sum * 10 + root.val); } }
337. House Robber III
小偷偷东西的问题。如果连在一起的房间都偷,则会触发警报。房间类似二叉树。求小偷能偷的最大数量
Example 1:
3 / \ 2 3 \ \ 3 1
Maximum amount of money the thief can rob = 3 + 3 + 1 = 7.
Example 2:
3 / \ 4 5 / \ \ 1 3 1
Maximum amount of money the thief can rob = 4 + 5 = 9.
思路:首先分析之前的错误解法,之前想的是每隔一层相加一次,但其实也有可能相隔很多层的和大于每隔一层的和,所以该解法错误。
说下正确解法,dfs的想法。bottom-up的思想,利用后序遍历。假设arr[0]代表这个节点我们可以抢劫的数量,arr[1]代表如果不抢这个节点我们能获得的数量。
arr[0]很容易想到,如果抢了该节点,那么arr[0]的值即为该节点的数量+不抢它的左子节点能获得的数量+不抢它的右子节点能获得的数量。
公式为arr[0]=root.val+left[1]+right[1].接下来考虑不抢该节点的情况,如果不抢该节点,那么对于该节点的左右子节点也分别有两种情况,抢或者不抢,
那么对应就是 arr[1]=Math.max(left[0],left[1]) + Math.max(right[0],right[1]).那么最大值即为arr[0]或者arr[1]其中的一个。
public class Solution { public int rob(TreeNode root) { int[] res = help(root); return Math.max(res[0], res[1]); } public int[] help(TreeNode root) { int[] arr = new int[2]; if (root == null) { return arr; } int[] left = help(root.left); int[] right = help(root.right); arr[0] = root.val + left[1] + right[1]; arr[1] = Math.max(left[0], left[1]) + Math.max(right[1], right[0]); return arr; } }
333. Largest BST Subtree
给定一个二叉树,找到该二叉树的最大二叉搜索子树,返回该树的尺寸。
思路:两种解法,第一种就是常规的判断每个节点为根时是否是bst,每遍历一个子树的跟节点,变会自根节点遍历至最下面的叶节点。因此时间复杂度为O(nlogn).
第二种解法:bottom-up,从下而上。Time Complexity: O(n). 每个点不会访问超过两遍. Space: O(logn).
第一种:
public class LargestBSTSubtree { public int largestBSTSubtree(TreeNode root) { if(isBST(root)) { return size(root); } return Math.max(largestBSTSubtree(root.left), largestBSTSubtree(root.right)); } public int size(TreeNode root) { if(root==null) { return 0; } return size(root.left)+size(root.right)+1; } public boolean isBST(TreeNode root) { return isBST(root,Integer.MIN_VALUE,Integer.MAX_VALUE); } public boolean isBST(TreeNode root, int min,int max){ if(root==null){ return true; } if(root.val<min||root.val>max){ return false; } return (isBST(root.left,min,root.val)&&isBST(root.right,root.val,max)); } }
第二种:
public class LargestBSTSubtreeII { public int largestBSTSubtree(TreeNode root) { int[] res={0}; helper(root,res); return res[0]; } public Node helper (TreeNode root,int [] res){ Node cur=new Node(); if(root==null){ cur.isBST=true; return cur; } Node left=helper(root.left,res); Node right=helper(root.right,res); if(left.isBST && root.val>left.max && right.isBST && root.val<right.min){ cur.isBST=true; cur.min=Math.min(root.val,left.min); cur.max=Math.max(root.val, right.max); cur.size=left.size+right.size+1; if(cur.size>res[0]){ res[0]=cur.size; } } return cur; } } class Node { boolean isBST; int max; int min; int size; public Node(){ isBST=false; min=Integer.MAX_VALUE; max=Integer.MIN_VALUE; size=0; } }
114. Flatten Binary Tree to Linked List
Given a binary tree, flatten it to a linked list in-place.
For example,
Given
1 / \ 2 5 / \ \ 3 4 6
The flattened tree should look like:
1 \ 2 \ 3 \ 4 \ 5 \ 6
思路:从根结点(root)找左子树(l)的最右子结点(cur),将root的右子树(r)接到cur的右子树上(cur的右子树为空),root的左子树整体调整为右子树,root的左子树赋空。
public class Solution { public void flatten(TreeNode root) { while(root!=null) { if(root.left!=null) { TreeNode cur=root.left; while(cur.right!=null) { cur=cur.right; } cur.right=root.right; root.right=root.left; root.left=null; } root=root.right; } } }
108. Convert Sorted Array to Binary Search Tree
给定一个排序好的升序数组,构建一个高度平衡的二叉搜树
思路:高度平衡,所以从中间二分构造。
public class Solution { public TreeNode sortedArrayToBST(int[] nums) { int left=0; int right=nums.length-1; return help(nums,left,right); } public TreeNode help(int[] nums,int left, int right) { if(left>right) { return null; } int mid=left+(right-left)/2; TreeNode root =new TreeNode(nums[mid]); root.left=help(nums,left,mid-1); root.right=help(nums,mid+1,right); return root; } }
116. Populating Next Right Pointers in Each Node
Given a binary tree
struct TreeLinkNode { TreeLinkNode *left; TreeLinkNode *right; TreeLinkNode *next; }
Populate each next pointer to point to its next right node. If there is no next right node, the next pointer should be set to NULL
.
Initially, all next pointers are set to NULL
.
Note:
- You may only use constant extra space.
- You may assume that it is a perfect binary tree (ie, all leaves are at the same level, and every parent has two children).
For example,
Given the following perfect binary tree,
1 / \ 2 3 / \ / \ 4 5 6 7
After calling your function, the tree should look like:
1 -> NULL / \ 2 -> 3 -> NULL / \ / \ 4->5->6->7 -> NULL
思路:将树的每一层节点用next串起来。这样每一层也会形成一个单链表。而每层的链表头,则是,根的左孩子,左孩子,左孩子。
利用双循环,外层循环,沿着根的左孩子,一直向下。内层循环,负责将下一层的节点串起来。即,将自己右孩子放到左孩子的next上,
而右孩子,则可通过自己的next指针,找到右邻居。
public class Solution { public void connect(TreeLinkNode root) { if(root==null) { return; } TreeLinkNode node; while(root!=null&&root.left!=null) { node=root; while(node!=null) { node.left.next=node.right; if(node.next!=null) { node.right.next=node.next.left; } node=node.next; } root=root.left; } } }
117. Populating Next Right Pointers in Each Node II
思路:与116题不同之处在于,给定的二叉树不是完全二叉树了,而是任意的二叉树,因此要判断左右子节点是否存在的情况。
/** * Definition for binary tree with next pointer. * public class TreeLinkNode { * int val; * TreeLinkNode left, right, next; * TreeLinkNode(int x) { val = x; } * } */ public class Solution { public void connect(TreeLinkNode root) { if(root==null) { return; } TreeLinkNode parent=root; TreeLinkNode pre=null; TreeLinkNode curHead=null; while(parent!=null) { TreeLinkNode lastCur=parent; while(lastCur!=null) { if(lastCur.left!=null) { if(curHead==null) { curHead=lastCur.left; pre=curHead; }else { pre.next=lastCur.left; pre=pre.next; } } if(lastCur.right!=null) { if(curHead==null) { curHead=lastCur.right; pre=curHead; }else { pre.next=lastCur.right; pre=pre.next; } } lastCur=lastCur.next; } parent=curHead; curHead=null; } } }
298: Binary Tree Longest Consecutive Sequence
Given a binary tree, find the length of the longest consecutive sequence path.
The path refers to any sequence of nodes from some starting node to any node in the tree along the parent-child connections. The longest consecutive path need to be from parent to child (cannot be the reverse).
For example,
1 \ 3 / \ 2 4 \ 5
Longest consecutive sequence path is 3-4-5
, so return 3
.
2 \ 3 / 2 / 1
Longest consecutive sequence path is 2-3
,not3-2-1
, so return 2
.
思路:递归,判断下一个值是否连续,不连续从下个值开始。
public class LongestConsecutiveSequence { int max = 1; public int longestConsecutive(TreeNode root) { if (root == null) return 0; rec(root, 1); return max; } private void rec(TreeNode n, int c) { if (n.left != null) { if (n.val + 1 == n.left.val) { rec(n.left, c + 1); max = Math.max(max, c + 1); } else { rec(n.left, 1); } } if (n.right != null) { if (n.val + 1 == n.right.val) { rec(n.right, c + 1); max = Math.max(max, c + 1); } else { rec(n.right, 1); } } } }
450. Delete Node in a BST
Given a root node reference of a BST and a key, delete the node with the given key in the BST. Return the root node reference (possibly updated) of the BST.
Basically, the deletion can be divided into two stages:
- Search for a node to remove.
- If the node is found, delete the node.
Note: Time complexity should be O(height of tree).
思路:三种情况,待删的节点没有左右子节点,待删的节点有一个子节点,待删的节点有两个子节点,其中有两个最麻烦,需要寻找后继节点。
public class DeleteNode { private TreeNode getSuccessor(TreeNode delNode) { TreeNode successorParent = delNode; TreeNode successor = delNode; TreeNode current = delNode.right; while (current != null) { successorParent = successor; successor = current; current = current.left; } if (successor != delNode.right) { successorParent.left = successor.right; successor.right = delNode.right; } return successor; } public TreeNode deleteNode(TreeNode root, int key) { if (root == null) { return null; } TreeNode current = root; TreeNode parent = root; boolean isLeftChild = true; while (current.val != key) { parent = current; if (key < current.val) { current = current.left; isLeftChild = true; } else { isLeftChild = false; current = current.right; } if (current == null) { return root; } } if (current.left == null && current.right == null) { if (current == root) { root = null; } else if (isLeftChild) { parent.left = null; } else { parent.right = null; } } else if (current.right == null) { if (current == root) { root = current.left; } else if (isLeftChild) { parent.left = current.left; } else { parent.right = current.left; } } else if (current.left == null) { if (current == root) { root = current.right; } else if (isLeftChild) { parent.left = current.right; } else { parent.right = current.right; } } else { TreeNode successor = getSuccessor(current); if (current == root) { root = successor; } else if (isLeftChild) { parent.left = successor; } else { parent.right = successor; } successor.left = current.left; } return root; } }
285. Inorder Successor in BST
Given a binary search tree and a node in it, find the in-order successor of that node in the BST.
Note: If the given node has no in-order successor in the tree, return null.
思路:其实就是上题的第三种情况。
public class Inorder_Successor_in_BST { public TreeNode inorderSuccessor(TreeNode root, TreeNode p) { if (root == null || p == null) { return null; } TreeNode successor = null; while (root != null) { if (p.val < root.val) { successor = root; root = root.left; } else { root = root.right; } } return successor; } }
98. Validate Binary Search Tree
给定一个树,判断该树是不是二叉搜索树
思路:易错点在于有可能左子树的一个右节点的值大于了根节点,或者是右子树的一个左节点的值小于了根节点。所以要有一个最大值和最小值的概念。
两种解法,第一种递归,第二种迭代。
第一种:
public class Solution { public boolean isValidBST(TreeNode root) { return isValid(root,Long.MIN_VALUE,Long.MAX_VALUE); } public boolean isValid(TreeNode root,long min, long max) { if(root==null) { return true; } if (root.val >= max || root.val <= min) { return false; } return isValid(root.left,min,root.val)&&isValid(root.right,root.val,max); } }
第二种:
public class Solution { public boolean isValidBST(TreeNode root) { if(root==null) { return true; } Stack<TreeNode> stack=new Stack<>(); TreeNode pre=null; while(!stack.isEmpty()||root!=null){ while(root!=null) { stack.push(root); root=root.left; } root=stack.pop(); if(pre!=null&&root.val<=pre.val) { return false; } pre=root; root=root.right; } return true; } }
96. Unique Binary Search Trees
Given n, how many structurally unique BST's (binary search trees) that store values 1...n?
For example,
Given n = 3, there are a total of 5 unique BST's.
1 3 3 2 1 \ / / / \ \ 3 2 1 1 3 2 / / \ \ 2 1 2 3
思路:典型的动态规划。找出0个值,1个值,2个值的情况,随便一个值为根,左子树*右子树即为该节点为根的数量。
public class Solution { public int numTrees(int n) { int[] dp=new int[n+1]; dp[0]=1; dp[1]=1; for(int i=2;i<=n;i++) { for(int j=1;j<=i;j++) { dp[i]+=dp[i-j]*dp[j-1]; } } return dp[n]; } }
95. Unique Binary Search Trees II
与上题不一样的地方在于要返回所有构建的树,而不是可构建的数量
public class Solution { public ArrayList<TreeNode> generateTrees(int n) { if (n == 0) { return new ArrayList<TreeNode>(); } return helper(1, n); } private ArrayList<TreeNode> helper(int left, int right) { ArrayList<TreeNode> res = new ArrayList<TreeNode>(); if (left > right) { res.add(null); return res; } for (int i = left; i <= right; i++) { ArrayList<TreeNode> leftList = helper(left, i - 1); ArrayList<TreeNode> rightList = helper(i + 1, right); for (int j = 0; j < leftList.size(); j++) { for (int k = 0; k < rightList.size(); k++) { TreeNode root = new TreeNode(i); root.left = leftList.get(j); root.right = rightList.get(k); res.add(root); } } } return res; } }
255. Verify Preorder Sequence in Binary Search Tree
Given an array of numbers, verify whether it is the correct preorder traversal sequence of a binary search tree.You may assume each number in the sequence is unique.
Follow up:
Could you do it using only constant space complexity?
思路:开始我总是想错,开始想错的地方在于给的树不是一个二叉搜索树,其实给的树是一个二叉搜索树,只不过这个数组可能顺序不是按这个二叉搜索树来的,是要判断这个数组,而不是判断这个树。先序的情况,
对于一个搜索二叉树的前序序列来说, 如果某段序列为一个递减序列, 说明这是一段沿着左子树的路径. 直到碰到一个比前一个大的值, 说明此时已经来到某个结点的右子树上了, 而此时可以得出一个此后序列的下界值, 也就是此后序列的任意一个值必须要比这个结点的父结点的值大, 因为对于搜索二叉树来说根节点左边的都比根节点小, 而根节点右边的都比根节点大, 所以既然现在已经来到某个结点(设为A)的右子树上, 那么此后任何结点的值必然比A的值大.
那么当我们碰到一个比之前结点大的值如何找到他的父结点呢? 可以借助一个栈, 即如果当前结点比栈顶元素小, 就入栈, 如果当前值大于栈顶值, 则让所有比当前结点小的值都出栈, 直到栈顶元素比当前结点大, 则最后一个出栈的比当前结点小的值就是当前结点的父结点, 我们只要在栈元素出栈的时候更新最小下界再将当前元素入栈即可. 另外这样的时间和空间复杂度都是O(n)。如果要o(1)的空间,那么可利用原数组去模拟栈。
思路一(栈):
public class VerifyPreorderinBST { public boolean verifyPreorder(int[] preorder) { Stack<Integer> stack = new Stack<>(); int min = Integer.MIN_VALUE; for (int i : preorder) { if (i < min) { return false; } while (!stack.isEmpty() && i > stack.peek()) { min = stack.pop(); } stack.push(i); } return true; } }
思路二:利用原数组模拟栈
public class Solution { public boolean verifyPreorder(int[] preorder) { int low = Integer.MIN_VALUE, index = -1; for (int i : preorder) { if (i < low) return false; while (index >= 0 && i > preorder[index]) low = preorder[index--]; preorder[++index] = i; } return true; } }
posted on 2016-12-04 23:19 Hennessy_Road 阅读(3061) 评论(0) 编辑 收藏 举报