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; } }