算法筆記:樹和分治+復(fù)雜度分析2

參考兩篇其他bolg總結(jié)的二叉樹:
https://github.com/xy7313/lintcode/blob/master/L3-BinaryTree/aboutTree.java

1. 樹和分治法的關(guān)系

  1. 分治法:divide-conquer
  2. 算法題目中,很大部分的樹題都可以用分治法的方法來解決
  3. 關(guān)于樹的題目比如
  4. Traverse in Binary tree: Preorder/Inorder/Postorder

前序遍歷(Pre-Order):根節(jié)點(diǎn)->左子樹->右子樹(NLR)
中序遍歷(In-Order):左子樹->根節(jié)點(diǎn)->右子樹(LNR)
后序遍歷(Post-Order):左子樹->右子樹->根節(jié)點(diǎn)(LRN)

舉個(gè)例子桑驱,比如中序的時(shí)候索烹,從以d為root的最下面子樹開始花竞,沒有左子樹,所以d嗓化,e,之后遍歷以b為root的子樹,應(yīng)該是b煌集,f,但f也是root捌省,所以這時(shí)候不到f苫纤,而是g,之后才f

中序纲缓,左中右的時(shí)候卷拘,如果該右節(jié)點(diǎn)了,右節(jié)點(diǎn)有子節(jié)點(diǎn)祝高,先去子節(jié)點(diǎn)栗弟,但是前序就是,該誰就先寫上工闺,之后再看他的子節(jié)點(diǎn)

leetcode-solution-java傳送門(github)(解析在文件夾下readme中):
postorder
preorder
inorder
my gist 代碼答案?jìng)魉烷T(go):
preorder
inorder

  1. DFS in Binary Tree(BFS : non-recursive)

  2. Basic operations on Binary Tree: Insert/Remove/Find/Validate

  3. 解決這些樹的搜索乍赫,遍歷颓屑,操作問題的方法:

  4. non-recursive : iteration,

  5. recursive : traverse, 全局,遞歸開始狀態(tài)是當(dāng)前狀態(tài)耿焊。但是全局變量有缺點(diǎn)揪惦,比如占內(nèi)存;全局內(nèi)可修改罗侯,易出錯(cuò)

preorder下遞歸三要素:
1. 遞歸的定義:把root為根的proorder放入result
2. 遞歸結(jié)束的時(shí)候器腋,該root的樹節(jié)點(diǎn)都加入了,當(dāng)前為null了
3. 遞歸的拆解:放root钩杰。放左子樹纫塌,放右子樹

  1. recursive : divide conquer。分治讲弄,沒有全局的變量措左,(自己本身就是遞歸的一部分)1. 分;2. 合
*. other diffs between traverse and divide-conquer:
1. result in parameter vs result in return value
2.  top down vs bottom up
  1. basic code of preorder, inorder, postorder 包括4中講的三種方法的實(shí)現(xiàn)

2. 復(fù)雜度分析

  1. tree problem time complexity:
    二叉樹通用時(shí)間復(fù)雜度計(jì)算公式 =(二叉樹的節(jié)點(diǎn)個(gè)數(shù)n * 每個(gè)節(jié)點(diǎn)的處理時(shí)間)
    比如divide conquer中都是if避除,處理時(shí)間就是O(1)怎披,總共就是O(n)
    notice!! only complete binary tree is logn, others , n/h
  2. 時(shí)間復(fù)雜度分析方法
    上篇回顧:
  3. binary search: 通過O(1)的時(shí)間,把規(guī)模為n的問題變?yōu)閚/2
T(n)=T(n/2)+O(1)
    =T(n/4)+O(1)+O(1)
…
    =T(1)+logn*O(1)
==> O(logn)
  1. more: 思考:通過O(n)的時(shí)間瓶摆,把規(guī)模為n的問題變?yōu)閚/2?
T(n)=T(n/2)+O(n)
      =T(n/4)+O(n)+O(n/2) 這里不能把O(n/2)約成O(n)凉逛,之后再約
…
     =T(1)+O(n+n/2+n/4+…)
     =T(1)+O(2n)
*因?yàn)椋?+2+4+8+…+n
=2+2+4+…+n-1
=2n
==>O(n)

本節(jié)兩個(gè)延伸問題:

  1. 通過O(n)的時(shí)間,把n的問題群井,變?yōu)榱藘蓚€(gè)n/2的問題状飞,復(fù)雜度是多少? (比如:quick sort代芜, merge sort)
T(n)=2 * T(n/2) + O(n)
       =2 * (2T(n/4) + O(n/2)) + O(n)
       =4 * T(n/4) + 2 * O(n/2) + O(n)
       =4 * (2T(n/8) + O(n/4)) + 2 * O(n/2) + O(n)
       =8 * T(n/8) + 4 * O(n/4) + 2 * O(n/2) + O(n)
...
        =2^k * T(n/2^k)+k * O(n)
k=logn
==>O(nlong) + n * T(1)
=O(nlong)
  1. 通過O(1)的時(shí)間蓉坎,把n的問題匣距,變成了兩個(gè)n/2的問題钟些,復(fù)雜度是多少?
T(n)=2 * T(n/2) + O(n)
       =2 * (2T(n/4) + O(n/2)) + O(1)
       =4 * T(n/4) + 2 * O(1) + O(1)
       =4 * (2T(n/8) + O(1)) + 2 * O(1) + O(1)
       =8 * T(n/8) + 4 * O(1) + 2 * O(1) + O(1)
...
        =2^k * T(n/2^k)+(1+2+4+...+2^k) * O(1)
k=logn(下面1+2+4+...上次證明過了)
==>(1+2+4+...+n) * O(1) + n * T(1)
=O(n)+n * T(1)
=O(n)

這個(gè)問題也可以通過畫樹的方式來看,得到第一層樹的時(shí)候做了O(1)的工作鸵荠,得到兩個(gè)n/2炬藤,下一步是做2 * O(1)的工作浙宜,得到4個(gè)n/4稍坯,... 最后還是(1+2+4+...+n) * O(1)得到最終結(jié)果O(1)

3. 解法

  1. 分析整棵樹在該問題上的結(jié)果 和左右兒子在該問題上的結(jié)果之間的聯(lián)系是什么
  2. Result類:一般在divide-conquer方法中需要返回一堆東西時(shí)使用:class ResultType { int var1, var2; }
  3. recursive

遞歸的基本思想是廣義地把規(guī)模大的問題轉(zhuǎn)化為規(guī)模小的相似的子問題或者相似的子問題集合來解決酬荞。廣義針對(duì)規(guī)模的搓劫,規(guī)模的縮小具體可以是指遞歸函數(shù)的參數(shù)瞧哟,也可以是其參數(shù)之一。相似是指解決大問題的方法和解決小問題的方法往往是同一個(gè)方法枪向,還可以是指解決子問題集的各子問題的方法是同一個(gè)方法勤揩。解決大問題的方法可以是由解決次規(guī)模問題的方法和解決剩余部分的方法組成,也可以是由一系列解決次規(guī)模問題的方法組成--http://zisong.me/post/suan-fa/ren-nao-li-jie-di-gui

4. 分類題目總結(jié)

  1. 比較簡(jiǎn)單的例子秘蛔,用divide-conquer和traverse都可以的:
  2. 題一:Binary Tree Paths:這個(gè)是兩種方法實(shí)現(xiàn):兩種方法都是遞歸陨亡,所以都要先判斷root==null和左右節(jié)點(diǎn)==null的情況傍衡,做適合的返回
    1. divide-conquer: 類似于先找左右子樹的path,然后和root分別組成path负蠕,所以上面說的左右節(jié)點(diǎn)==null的情況要把當(dāng)前節(jié)點(diǎn)加入蛙埂,左右子樹的path也通過該方法獲得,最后把左子樹的所有path遍歷都加入root,右邊同理遮糖,得到path
    2. traverse:通常是站在root绣的,向左右走,所以root先加入path欲账,走到盡頭的路上一次加入節(jié)點(diǎn)屡江,最后,左右節(jié)點(diǎn)==null的時(shí)候赛不,要把當(dāng)前已生成的path加入到結(jié)果集中惩嘉。
//divide-conquer
           public List<String> binaryTreePaths(TreeNode root) {
                List<String> paths = new ArrayList<>();
                if(root==null) return paths;
                if(root.left == null && root.right == null){
                    paths.add(root.val+"");
                    return paths;
                }
                List<String> left =binaryTreePaths(root.left);
                List<String> right =binaryTreePaths(root.right);
                //left==[], skip for()
                for(String l : left){
                    paths.add(root.val+"->"+l);
                }
                for(String r : right){
                    paths.add(root.val+"->"+r);
                }
                return paths;
            }
    // helper-traverse
         public List<String> binaryTreePaths(TreeNode root) {
            List<String> result = new ArrayList<>();
            if(root==null) return result;
            helper(root,String.valueOf(root.val),result);
            return result;
        }
        private void helper(TreeNode root, String path, List<String> result){
            if(root==null) return;
            if(root.left == null && root.right == null){
                result.add(path);
            }
            if(root.left!=null){
                helper(root.left, path+"->"+String.valueOf(root.left.val), result);
            }
            if(root.right!=null){
                helper(root.right, path+"->"+String.valueOf(root.right.val), result);
            }
        }
  1. 題二:Maximum Depth of Binary Tree:左邊取最大,右邊取最大踢故,結(jié)果是這兩者之中的較大值+1(root)文黎,左右兩邊取最大就是遞歸調(diào)用自己,divide-conquer比較簡(jiǎn)單
  2. 題三:Minimum Subtree:跟上面思路一樣殿较,都取最小臊诊,區(qū)別在于上面是要返回最大值int的,這個(gè)要返回node斜脂,所以用一樣的分治法需要new class, Result{node; min; sum}來存儲(chǔ)我們需要的結(jié)果抓艳。 也可以用traverse+全局變量+divide-conquer的方法做
    貼一下這兩個(gè)題的divide-conquer方法
//max depth
public int maxDepth(TreeNode root) {
        int dep  = 0;
        // null or leaf
        if (root == null) {
            return dep;
        }
        // Divide
        int left = maxDepth(root.left);
        int right = maxDepth(root.right);
        // Conquer
        return Math.max(left,right)+1;    
    }
// min subtree
public class Solution {
    class Result{
        int sum;
        int min;
        TreeNode minroot;
        public Result(int sum,  TreeNode minroot,int min){
            this.sum = sum;
            this.minroot=minroot;
            this.min = min;
        }
    }
    
    public TreeNode findSubtree(TreeNode root){
        return helper(root).minroot;
    }
    private Result helper(TreeNode root){
        if(root==null) return new Result(0,null,Integer.MAX_VALUE);
        //divide
        Result left = helper(root.left);
        Result right = helper(root.right);
        //conquer
        Result r = new Result(root.val,root,root.val);
        r.sum = root.val+left.sum+right.sum;
        if(r.sum<left.min&&r.sum<right.min){
            return new Result(r.sum,r.minroot,r.sum);
        }else if(r.sum>left.min&&left.min<right.min){
            return new Result(r.sum,left.minroot,left.min);
        }else{
            return new Result(r.sum,right.minroot,right.min);
        }
    }
}
  1. 用Result類的一些例子(包括上面的min subtree的方法)
  2. Balanced Binary Tree
    首先看定義:左子樹是平衡的,右子樹是平和的帚戳,左右高度差距不大于1玷或,或者說高度是想同的,三個(gè)條件
  3. 第一種解法看起來代碼短小精悍片任,很好偏友,但這個(gè)解法最難的地方應(yīng)該是maxdepth這個(gè)方法的返回值部分,最后如果遍歷到目前為止对供,是balanced的話位他,記錄當(dāng)前的height就是通過return語句做的。應(yīng)該是因?yàn)檫@里只要求返回Boolean产场,所以可以省個(gè)Result類
  4. Result類實(shí)現(xiàn)鹅髓,兩種解法的思路其實(shí)就一樣,寫過第一種京景,第二種就可以自己寫出來了窿冯,需要注意一點(diǎn)的是當(dāng) unbalanced, maxdepth怎么表示合適确徙,我選了0醒串,也可以是-1执桌,可以通過交流來選擇合適的值。
public class Solution {
            public boolean isBalanced(TreeNode root) {
                return maxDepth(root)!=-1;
            }
            private int maxDepth(TreeNode root){
                if(root==null) return 0;
            }
            
            int left = maxDepth(root.left);
            int right = maxDepth(root.right);
            if(left==-1 || right==-1 || Math.abs(left-right)>1){
                return -1;
            }
            return Max.max(left,right)+1;
        }
//with Result class
public class Solution {
            class Result{
                int maxdepth;
                boolean isBalanced;
                public Result(boolean isBalanced, int maxdepth){
                    this.isBalanced = isBalanced;
                    this.maxdepth = maxdepth;
                }
            }

            public boolean isBalanced(TreeNode root) {
                return helper(root).isBalanced;
            }
            private Result helper(TreeNode root){
                if(root==null) return new Result(true,0);
                Result left = helper(root.left);
                Result right = helper(root.right);
                if(!left.isBalanced || !right.isBalanced || Math.abs(left.maxdepth-right.maxdepth)>1){
                    return new Result(false,0);
                }
                return new Result(true,Math.max(left.maxdepth,right.maxdepth)+1);
            }
        }
  1. Validate Binary Search Tree
    一個(gè)traverse方法芜赌,一個(gè)divide conquer仰挣,前者代碼更簡(jiǎn)潔,后者思路更清晰缠沈,要注意一點(diǎn)是validate函數(shù)中如果沒有違背BST規(guī)則的話最后返回的是更新的Result椎木,更新max用right.max, 更新min用left.min不要弄錯(cuò)了
class Result{
                boolean is_bst;
                int maxValue, minValue;
                Result(boolean is_bst, int maxValue, int minValue) {
                    this.is_bst = is_bst;
                    this.maxValue = maxValue;
                    this.minValue = minValue;
                }
            }
        public class Solution {
            
            public boolean isValidBST(TreeNode root) {
                Result r = validate(root);
                return r.is_bst;
            }
            private Result validate(TreeNode root){
                //要想好這里返回什么
                if(root==null ) return new Result(true,Integer.MIN_VALUE,Integer.MAX_VALUE);
                
                Result left = validate(root.left);
                Result right = validate(root.right);
                
                if(!left.is_bst || !right.is_bst){
                    return new Result(false,0,0);
                }
                if(root.left!=null&& left.maxValue>=root.val){
                    return new Result(false,0,0);
                }
                if(root.right!=null&& right.minValue<=root.val){
                    return new Result(false,0,0);
        
                }
                return new Result(true,
                                    Math.max(right.maxValue,root.val),
                                    Math.min(left.minValue,root.val)
                                    );
            }
        }
  1. Subtree with Maximum Average
    九章答案里只有traverse+divide/conquer的方法,和上面那個(gè)min subtree很類似博烂,在用全局變量的基礎(chǔ)上加入了Result class香椎,全局變量也有一個(gè)是Result 類型。一個(gè)小trick是求ave的時(shí)候用了 轉(zhuǎn)換成乘法的方式禽篱,避免除法帶來的誤差等各種問題
        private TreeNode subtree = null;
        private ResultType subtreeResult = null;
        private class ResultType {
            public int sum, size;
            public ResultType(int sum, int size) {
                this.sum = sum;
                this.size = size;
            }
        }   

        public TreeNode findSubtree2(TreeNode root) {
            helper(root);
            return subtree;
        }

        private ResultType helper(TreeNode root) {
            if (root == null) {
                return new ResultType(0, 0);
            }
            
            ResultType left = helper(root.left);
            ResultType right = helper(root.right);
            ResultType result = new ResultType(
                left.sum + right.sum + root.val,
                left.size + right.size + 1
            );
            
            if (subtree == null ||
                subtreeResult.sum * result.size < result.sum * subtreeResult.size
            ) {
                subtree = root;
                subtreeResult = result;
            }
            return result;
        }
  1. Flatten Binary Tree to Linked List
    思路是:flattern左子樹和右子樹畜伐,然后root—>left.head, left.tail—>right.head 。就是左子樹flatten躺率,右子樹flatten玛界,那我們想把root,左悼吱,右三部分拼起來就需要root->left.first--left.last->right.first
    有三種方法慎框,都看一下

  2. Binary Tree Longest Consecutive Sequence (google onsite)
    比較簡(jiǎn)單的一種traverse+divide conquer方法:一個(gè)全局變量longest和一個(gè)helper方法

private int helper(TreeNode root, TreeNode parent, int lengthWithoutRoot) {
    if (root == null) {
        return 0;
    }
    int length = (parent != null && parent.val + 1 == root.val)
        ? lengthWithoutRoot + 1
        : 1;
    int left = helper(root.left, root, length);
    int right = helper(root.right, root, length);
    return Math.max(length, Math.max(left, right));
}
  1. LCA系列:給兩個(gè)node A, B 找最近公共祖先
  2. 新手版,簡(jiǎn)單的divide+conquer后添,因?yàn)檫@里不用管是不是都存在笨枯,所以后面那里直接返回left,right遇西∠诰看起來是種很取巧的方法
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode node1, TreeNode node2) {
            if (root == null || root == node1 || root == node2) {
                return root;
            }
            // Divide
            TreeNode left = lowestCommonAncestor(root.left, node1, node2);
            TreeNode right = lowestCommonAncestor(root.right, node1, node2);        
            // Conquer
            if (left != null && right != null) {
                return root;
            } 
            if (left != null) {
                return left;
            }
            if (right != null) {
                return right;
            }
            return null;
        }
2. 包含parent指針:找到給定的A,B兩點(diǎn)到root的path粱檀,然后從root開始一起遍歷兩條path洲敢,path上最后一個(gè)相同的點(diǎn),就是LCA茄蚯。這個(gè)思路很直接
        public ParentTreeNode lowestCommonAncestorII(ParentTreeNode root,
                                                        ParentTreeNode A,
                                                        ParentTreeNode B) {
                if(root==null) return null;
                ArrayList<ParentTreeNode> left = pathToRoot(A);
                ArrayList<ParentTreeNode> right = pathToRoot(B);
                int idxl = left.size()-1;
                int idxr = right.size()-1;
                ParentTreeNode lca = null;
                while (idxl >= 0 && idxr >= 0) {
                    if(left.get(idxl)!=right.get(idxr)) break;
                    lca = left.get(idxl);
                    idxl--;
                    idxr--;
                }
                return lca;
            }
            private ArrayList<ParentTreeNode> pathToRoot(ParentTreeNode node){
                ArrayList<ParentTreeNode> path = new ArrayList<ParentTreeNode>();
                //理解思路情況下這里寫錯(cuò)了压彭,判斷了parent并添加parent,不對(duì)渗常。
                while(node!=null ){
                    path.add(node);
                    node = node.parent;
                }
                return path;
            }
3. 如果給的節(jié)點(diǎn)有一個(gè)不存在壮不,返回null(新手版返回存在的那個(gè)節(jié)點(diǎn))這個(gè)方法反正想不到,而且判斷是否存在那里凳谦,很繞忆畅。抄的lintcode答案。尸执。家凯。
/**
        * Definition of TreeNode:
        * public class TreeNode {
        *     public int val;
        *     public TreeNode left, right;
        *     public TreeNode(int val) {
        *         this.val = val;
        *         this.left = this.right = null;
        *     }
        * }
        */
        // check if a exists, b exists
        class Result {
            public boolean a_exist, b_exist;
            public TreeNode node;
            Result(boolean a, boolean b, TreeNode n) {
                a_exist = a;
                b_exist = b;
                node = n;
            }
        }
        public class Solution {
            public TreeNode lowestCommonAncestor3(TreeNode root, TreeNode A, TreeNode B) { 
                Result re = help(root,A,B);
                if(re.a_exist && re.b_exist) return re.node;
                else return null;
            }
            private Result help(TreeNode root, TreeNode A, TreeNode B){
                if(root==null) return new Result(false, false, null);
                
                Result left = help(root.left, A, B);
                Result right = help(root.right, A, B);
                
                boolean aexi = left.a_exist || right.a_exist || root==A;
                boolean bexi = left.b_exist || right.b_exist || root==B;
                
                if(root==A || root==B || (left.node!=null && right.node!=null)){
                    return new Result(aexi, bexi, root);
                }
                if(left.node!=null) return new Result(aexi, bexi, left.node);
                if(right.node!=null) return new Result(aexi, bexi, right.node);             
                return new Result(aexi, bexi, null);
            }
        }
  1. Binary Tree Path Sum系列:給一個(gè)target,找root到leaf的一條path和==target
  2. 正常版
             public List<List<Integer>> binaryTreePathSum(TreeNode root, int target) {
                    List<List<Integer>> result = new ArrayList<>();
                    if (root == null) {
                        return result;
                    }
                    
                    ArrayList<Integer> path = new ArrayList<Integer>();
                    //A valid path is from root node to any of the leaf nodes. So we always need to add root in each path.
                    path.add(root.val);
                    helper(root, path, root.val, target, result);
                    return result;
                }
                //preorder如失、DFS + backtracking
                private void helper(TreeNode root,
                                    ArrayList<Integer> path,
                                    int sum,
                                    int target,
                                    List<List<Integer>> result) {
                                        
                    // 遞歸的出口:meet leaf && sum==target
                    if (root.left == null && root.right == null) {
                        if (sum == target) {
                            result.add(new ArrayList<Integer>(path));
                        }
                        return;
                    }
                    //遞歸的拆解:分別去左右子樹绊诲,用來算sum
                    if (root.left != null) {
                        path.add(root.left.val);
                        helper(root.left, path, sum + root.left.val, target, result);
                        //back-tracking, delete the last elementm of path to contruct new path
                        path.remove(path.size() - 1);
                    }
                    if (root.right != null) {
                        path.add(root.right.val);
                        helper(root.right, path, sum + root.right.val, target, result);
                        path.remove(path.size() - 1);
                    }
                }
1. The path does not need to start or end at the root or a leaf, but it must go in a straight line down.

思路是:
到每個(gè)點(diǎn)的時(shí)候,都幾下所有到這個(gè)點(diǎn)的path褪贵,包括不從頭出發(fā)的掂之,只要是在當(dāng)前點(diǎn)end的path,都存脆丁。比如1--2--4這樣的樹(請(qǐng)腦補(bǔ)成一棵樹)世舰,走到2, store[[],[2],[1,2]],同樣的走到4, store [[],[4],[1,2,4],[2,4]]. 其他見代碼注釋槽卫,也是個(gè)自己寫估計(jì)寫不出來的方法

public List<List<Integer>> binaryTreePathSum2(TreeNode root, int target) {
                List<List<Integer>> results = new ArrayList<List<Integer>>();
                //buffer這名字意味著這個(gè)東西是起輔助作用的,幫助記錄path跟压,從而在得到target的時(shí)候可以在buffer中倒推找到validpath
                ArrayList<Integer> buffer = new ArrayList<Integer>();
                if (root == null)
                    return results;
                findSum(root, target, buffer, 0, results);
                return results;
            }
            //遞歸的定義:level的意思顯而易見,但不太知道為什么用level
            public void findSum(TreeNode head, int sum, ArrayList<Integer> buffer, int level, List<List<Integer>> results) {
                if (head == null) return;
                //因?yàn)楹竺孢€要傳入sum歼培,所以copy一個(gè)sum值來操作而不在原數(shù)據(jù)上操作震蒋。這里對(duì)tmp sum的操作是-=,target-=nodes.vals直到等于0時(shí)躲庄,就說明這幾個(gè)nodes sum==target
                int tmp = sum;
                buffer.add(head.val);
                for (int i = level;i >= 0; i--) {
                    tmp -= buffer.get(i);
                    if (tmp == 0) {
                        //在buf中回找validPath
                        List<Integer> validPath = new ArrayList<Integer>();
                        for (int j = i; j <= level; ++j)
                            validPath.add(buffer.get(j));
                        results.add(validPath);
                    }
                }
                findSum(head.left, sum, buffer, level + 1, results);
                findSum(head.right, sum, buffer, level + 1, results);
                buffer.remove(buffer.size() - 1);
            }
2. the path could be start and end at any node in the tree.(n個(gè)節(jié)點(diǎn)的樹查剖,任意兩點(diǎn)選路徑共有n choose 2條,可以暴力解噪窘,枚舉所有兩點(diǎn)之間有路徑的情況笋庄,以當(dāng)前點(diǎn)分左右所有節(jié)點(diǎn)兩部分,兩部分兩兩配對(duì))

可以拐彎的follow-up倔监,關(guān)注點(diǎn)都在拐點(diǎn)无切,拐點(diǎn)前后,必是直上直下丐枉,所以可以用正常方法得到哆键,之后在拼接(代碼自己并不能獨(dú)立寫,不貼了)

5. 樹和分治總結(jié)

二叉樹
給出一棵Binary Tree的字符串表示瘦锹,比如[1,[2,3]]籍嘹,還原這棵二叉樹(高頻)
給出一棵Binary Tree的先序和中序遍歷序列,還原這棵二叉樹
給出一棵Binary Tree弯院,按照深度(同樣深度從左往右)遍歷并輸出結(jié)果
給出一棵Binary Tree辱士,輸出每一條從根節(jié)點(diǎn)到葉子節(jié)點(diǎn)的路徑
給出一棵Binary Tree,輸出與之鏡面對(duì)稱的二叉樹
給出兩棵Binary Tree听绳,判斷這兩棵二叉樹是否完全一樣(形狀和每個(gè)點(diǎn)的value都要相同才算完全一樣)
給出兩棵Binary Tree颂碘,A和B,判斷B是否為A的子樹
分治法
求一棵二叉樹的最大深度(分治思想的簡(jiǎn)單應(yīng)用)
給出一棵Binary Tree椅挣,求出這棵二叉樹上和最大的路徑
給出一棵Binary Search Tree头岔,問是否是Balanced Binary Search Tree
合并k個(gè)排好序的List(高頻)
求一個(gè)Array中的中位數(shù)(高頻塔拳,partition方法)
給出兩個(gè)排好序的List,輸出這兩個(gè)序列中的中位數(shù)峡竣,如果存在兩個(gè)中位數(shù)則輸出這兩個(gè)數(shù)的平均數(shù)
給出一個(gè)Array靠抑,求出Array中的每個(gè)元素的右邊比其小的元素個(gè)數(shù)(歸并排序應(yīng)用)
給出一個(gè)平面上的若干個(gè)點(diǎn),求其中最近的兩個(gè)點(diǎn)的距離(要求時(shí)間復(fù)雜度小于n^2)
給出一個(gè)n*n的棋盤适掰,n是2的冪颂碧,開始時(shí)這個(gè)棋盤上只有一個(gè)格子是黑色的,其他均是白色的±嗬耍現(xiàn)在需要用一個(gè)黑色的L型去填滿這個(gè)棋盤载城,求一種填滿的方案(要求時(shí)間復(fù)雜度盡可能低)
Reference

  1. http://www.geeksforgeeks.org/category/algorithm/divide-and-conquer/

6. 其他相關(guān)問題

? Binary Search Tree Iterator
? http://www.lintcode.com/problem/binary-search-tree-iterator
? http://www.jiuzhang.com/solutions/binary-search-tree-iterator
? In-order Successor in Binary Search Tree
? http://www.lintcode.com/problem/inorder-successor-in-binary-search-tree/
? http://www.jiuzhang.com/solutions/inorder-successor-in-binary-search-tree/
? Search Range in Binary Search Tree
? http://www.lintcode.com/problem/search-range-in-binary-search-tree/
? Insert Node in a Binary Search Tree
? http://www.lintcode.com/problem/insert-node-in-a-binary-search-tree/
? Remove Node in a Binary Search Tree
? http://www.lintcode.com/problem/remove-node-in-binary-search-tree/
? http://www.mathcs.emory.edu/~cheung/Courses/171/Syllabus/9-BinTree/BST-delete.html

  1. 求二叉樹中的節(jié)點(diǎn)個(gè)數(shù):
    getNodeNumRec(遞歸),getNodeNum(迭代)
  2. 求二叉樹的深度:
    getDepthRec(遞歸)费就,getDepth
  3. 分層遍歷二叉樹(按層次從上往下诉瓦,從左往右):
    levelTraversal, levelTraversalRec(遞歸解法)
  4. 將二叉查找樹變?yōu)橛行虻碾p向鏈表:
    convertBST2DLLRec, convertBST2DLL

如果二叉查找樹不為空:
如果左子樹為空,對(duì)應(yīng)雙向有序鏈表的第一個(gè)節(jié)點(diǎn)是根節(jié)點(diǎn)受楼,左邊不需要其他操作垦搬;
如果左子樹不為空,轉(zhuǎn)換左子樹艳汽,二叉查找樹對(duì)應(yīng)雙向有序鏈表的第一個(gè)節(jié)點(diǎn)就是左子樹轉(zhuǎn)換后雙向有序鏈表的第一個(gè)節(jié)點(diǎn)猴贰,同時(shí)將根節(jié)點(diǎn)和左子樹轉(zhuǎn)換后的雙向有序鏈 表的最后一個(gè)節(jié)點(diǎn)連接;
如果右子樹為空河狐,對(duì)應(yīng)雙向有序鏈表的最后一個(gè)節(jié)點(diǎn)是根節(jié)點(diǎn)米绕,右邊不需要其他操作;
如果右子樹不為空馋艺,對(duì)應(yīng)雙向有序鏈表的最后一個(gè)節(jié)點(diǎn)就是右子樹轉(zhuǎn)換后雙向有序鏈表的最后一個(gè)節(jié)點(diǎn)栅干,同時(shí)將根節(jié)點(diǎn)和右子樹轉(zhuǎn)換后的雙向有序鏈表的第一個(gè)節(jié)點(diǎn)連 接。

  1. 求二叉樹第K層的節(jié)點(diǎn)個(gè)數(shù):
    getNodeNumKthLevelRec, getNodeNumKthLevel

(1)如果二叉樹為空或者k<1返回0
(2)如果二叉樹不為空并且k==1捐祠,返回1
(3)如果二叉樹不為空且k>1碱鳞,返回左子樹中k-1層的子節(jié)點(diǎn)個(gè)數(shù)與右子樹k-1層子節(jié)點(diǎn)個(gè)數(shù)之和(這個(gè)描述有點(diǎn)奇怪)

private int GetNodeNumKthLevel(BinaryTreeNode Root, int k){
    if(Root == NULL || k < 1) return 0;
    if(k == 1) return 1;
    int numLeft = GetNodeNumKthLevel(root.left, k-1); 
    int numRight = GetNodeNumKthLevel(root.right, k-1);
    return (numLeft + numRight);
}
  1. 求二叉樹中葉子節(jié)點(diǎn)的個(gè)數(shù):
    (1)如果二叉樹為空,返回0
    (2)如果二叉樹不為空且左右子樹為空踱蛀,返回1
    (3)如果二叉樹不為空窿给,且左右子樹不同時(shí)為空崩泡,返回左子樹中葉子節(jié)點(diǎn)個(gè)數(shù)加上右子樹中葉子節(jié)點(diǎn)個(gè)數(shù)
  2. 判斷兩棵二叉樹是否相同的樹:
    (1)如果兩棵二叉樹都為空,返回真
    (2)如果兩棵二叉樹一棵為空角撞,另一棵不為空,返回假
    (3)如果兩棵二叉樹都不為空热康,如果對(duì)應(yīng)的左子樹和右子樹都同構(gòu)返回真,其他返回假
  3. 求二叉樹的鏡像(破壞和不破壞原來的樹兩種情況):
    (1)如果二叉樹為空褐隆,返回空
    (2)如果二叉樹不為空剖踊,求左子樹和右子樹的鏡像,然后交換左子樹和右子樹
    判斷兩個(gè)樹是否互相鏡像
  4. 求二叉樹中節(jié)點(diǎn)的最大距離:
    (1)如果二叉樹為空衫贬,返回0德澈,同時(shí)記錄左子樹和右子樹的深度,都為0
    (2)如果二叉樹不為空固惯,最大距離要么是左子樹中的最大距離梆造,要么是右子樹中的最大距離,要么是左子樹節(jié)點(diǎn)中到根節(jié)點(diǎn)的最大距離+右子樹節(jié)點(diǎn)中到根節(jié)點(diǎn)的最大距離葬毫,同時(shí)記錄左子樹和右子樹節(jié)點(diǎn)中到根節(jié)點(diǎn)的最大距離镇辉。
  5. 由前序遍歷序列和中序遍歷序列重建二叉樹:
    rebuildBinaryTreeRec
  6. 判斷二叉樹是不是完全二叉樹:
    若設(shè)二叉樹的深度為h,除第 h 層外贴捡,其它各層 (1~h-1) 的結(jié)點(diǎn)數(shù)都達(dá)到最大個(gè)數(shù)忽肛,第 h 層所有的結(jié)點(diǎn)都連續(xù)集中在最左邊,這就是完全
    二叉樹烂斋。
    有如下算法屹逛,按層次(從上到下,從左到右)遍歷二叉樹汛骂,當(dāng)遇到一個(gè)節(jié)點(diǎn)的左子樹為空時(shí)罕模,則該節(jié)點(diǎn)右子樹必須為空,且后面遍歷的節(jié)點(diǎn)左
    右子樹都必須為空帘瞭,否則不是完全二叉樹淑掌。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蝶念,隨后出現(xiàn)的幾起案子抛腕,更是在濱河造成了極大的恐慌,老刑警劉巖祸轮,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兽埃,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡适袜,警方通過查閱死者的電腦和手機(jī)柄错,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人售貌,你說我怎么就攤上這事「疑欤” “怎么了池颈?”我有些...
    開封第一講書人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長琢歇。 經(jīng)常有香客問我李茫,道長魄宏,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任名秀,我火速辦了婚禮匕得,結(jié)果婚禮上汁掠,老公的妹妹穿的比我還像新娘考阱。我一直安慰自己乞榨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著河质,像睡著了一般震叙。 火紅的嫁衣襯著肌膚如雪捐友。 梳的紋絲不亂的頭發(fā)上匣砖,一...
    開封第一講書人閱讀 51,115評(píng)論 1 296
  • 那天猴鲫,我揣著相機(jī)與錄音,去河邊找鬼姻几。 笑死蛇捌,一個(gè)胖子當(dāng)著我的面吹牛络拌,可吹牛的內(nèi)容都是我干的混萝。 我是一名探鬼主播萍恕,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼崭倘,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了登澜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤谴仙,失蹤者是張志新(化名)和其女友劉穎晃跺,沒想到半個(gè)月后掀虎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體烹玉,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年继效,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瑞信。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片喧伞。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡潘鲫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出状植,到底是詐尸還是另有隱情,我是刑警寧澤必怜,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站膏执,受9級(jí)特大地震影響更米,放射性物質(zhì)發(fā)生泄漏征峦。R本人自食惡果不足惜眶痰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一因宇、第九天 我趴在偏房一處隱蔽的房頂上張望察滑。 院中可真熱鬧贺辰,春花似錦饲化、人聲如沸吃靠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽越走。三九已至泣栈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間弥姻,已是汗流浹背南片。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留庭敦,地道東北人疼进。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像秧廉,于是被迫代替她去往敵國和親伞广。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容