只介紹先序遍歷:node
(1) 第一種方法是使用stack的結構算法
(2) 主要要理解後面的分治法ide
Version 0: Non-Recursion (Recommend) /** * Definition for binary tree * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ public class Solution { public List<Integer> preorderTraversal(TreeNode root) { Stack<TreeNode> stack = new Stack<TreeNode>(); List<Integer> preorder = new ArrayList<Integer>(); if (root == null) { return preorder; } stack.push(root); while (!stack.empty()) { TreeNode node = stack.pop(); preorder.add(node.val); if (node.right != null) { stack.push(node.right); } if (node.left != null) { stack.push(node.left); } } return preorder; } } //Version 1: Traverse public class Solution { public ArrayList<Integer> preorderTraversal(TreeNode root) { ArrayList<Integer> result = new ArrayList<Integer>(); traverse(root, result); return result; } // 把root爲跟的preorder加入result裏面 private void traverse(TreeNode root, ArrayList<Integer> result) { if (root == null) { return; } result.add(root.val); traverse(root.left, result); traverse(root.right, result); } } //Version 2: Divide & Conquer public class Solution { public ArrayList<Integer> preorderTraversal(TreeNode root) { ArrayList<Integer> result = new ArrayList<Integer>(); // null or leaf if (root == null) { return result; } // Divide ArrayList<Integer> left = preorderTraversal(root.left); ArrayList<Integer> right = preorderTraversal(root.right); // Conquer result.add(root.val); result.addAll(left); result.addAll(right); return result; } }
三個例子:this
歸併排序spa
快速排序code
大多數的二叉樹問題blog
歸併排序和快速排隊都是典型的分治法。排序
歸併排序:強調先局部有序,再歸併爲總體有序. 最差時間複雜度和平均複雜度都是log(n)*n,可是有問題就是在歸併的時候須要額外的空間,同時它仍是一個穩定(什麼是穩定,相同元素的位置是否須要移動)的排序算法。隊列
快速排序:強調先總體有序,再partition爲局部有序,最差狀況是n*n,平均複雜度是log(n)*n,不須要額外空間,須要swap操做,是一個不穩定的排序算法。rem
數學概括法是成立的,一切都是創建數學概括法是成立以及空集存在的可能性上創建的。
二叉樹問題:
(1) 最大深度:
depth(TreeNode) = max(depth(left), depth(right))+1
(2) 是不是平衡樹?
是平衡樹:
leftChild是平衡樹
rightChild是平衡樹
| depth(leftChild) - depth(rightChild) | <1
class ResultType { public boolean isBalanced; public int maxDepth; public ResultType(boolean isBalanced, int maxDepth) { this.isBalanced = isBalanced; this.maxDepth = maxDepth; } }
public class Solution { /** * @param root: The root of binary tree. * @return: True if this Binary tree is Balanced, or false. */ public boolean isBalanced(TreeNode root) { return helper(root).isBalanced; } private ResultType helper(TreeNode root) { if (root == null) { return new ResultType(true, 0); } ResultType left = helper(root.left); ResultType right = helper(root.right); // subtree not balance if (!left.isBalanced || !right.isBalanced) { return new ResultType(false, -1); } // root not balance if (Math.abs(left.maxDepth - right.maxDepth) > 1) { return new ResultType(false, -1); } return new ResultType(true, Math.max(left.maxDepth, right.maxDepth) + 1); } }
(3) 最近公共祖先問題
問題描述:給2個節點n1 n2 (n1,n2都存在),尋找兩者最近的公共祖先.
find(root, n1, n2)
分治法,從根出發,自頂向下,從左右2個子樹上去尋找.
先考慮單子樹的狀況:
合併2個子樹的尋找結果 leftResult和rightResult.
若是leftResult非空,代表至少出現一個或者出現2個.
同理,若是rightResult非空,代表至少出現一個或者出現2個.
所以:
若是都非空,表示一邊一個,返回root
若是一邊爲空,另外一邊非空,返回非空的那個
若是都爲空,沒有找到? 和假設不合,不考慮
代碼以下:
// 在root爲根的二叉樹中找A,B的LCA: // 若是找到了就返回這個LCA // 若是隻碰到A,就返回A // 若是隻碰到B,就返回B // 若是都沒有,就返回null /** * 這裏有個假設, node1和node2都必須存在. 假如不存在下面的方法是有問題的. * * @param root 以root爲搜索基礎節點 * @param node1 要尋找的node1 * @param node2 要尋找的node2 * @return 兩者的最近公共祖先 */ public TreeNode lowestCommonAncestor(TreeNode root, TreeNode node1, TreeNode node2) { if (root == null) { return null; } if (root == node1) { return node1; } if (root == node2) { return node2; } TreeNode leftResult = lowestCommonAncestor(root.left, node1, node2); TreeNode rightResult = lowestCommonAncestor(root.left, node1, node2); if (leftResult == null && rightResult == null) { return null; } else if (leftResult != null & rightResult != null) { return root; } else if (leftResult == null && rightResult != null) { return rightResult; } else { return leftResult; } }
總結使用分治法解決的大體代碼模板:
public class Solution { public void traverse(TreeNode root) { if (root == null) { return; } // do something with root traverse(root.left); // do something with root traverse(root.right); // do something with root } } Tempate 2: Divide & Conquer public class Solution { public ResultType traversal(TreeNode root) { // null or leaf if (root == null) { // do something and return; } // Divide ResultType left = traversal(root.left); ResultType right = traversal(root.right); // Conquer ResultType result = Merge from left and right. return result; } }
三種方式:
2個隊列
1個隊列 + Dummy Node
1個隊列 (best)
public class Solution { /** * @param root: The root of binary tree. * @return: buttom-up level order a list of lists of integer */ public ArrayList<ArrayList<Integer>> levelOrderBottom(TreeNode root) { ArrayList<ArrayList<Integer>> result = new ArrayList<>(); if (root == null) { return result; } Queue<TreeNode> queue = new LinkedList<TreeNode>(); queue.offer(root); while (!queue.isEmpty()) { int size = queue.size(); ArrayList<Integer> level = new ArrayList<>(); for (int i = 0; i < size; i++) { TreeNode head = queue.poll(); level.add(head.val); if (head.left != null) { queue.offer(head.left); } if (head.right != null) { queue.offer(head.right); } } result.add(level); } Collections.reverse(result); return result; } }
左子樹比根節點小, 且右子樹比根節點大.
(1) 判斷是不是bst
(2) range query
(3) remove node
(4) Iterator