算法筆記:樹和分治+復雜度分析2

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

1. 樹和分治法的關系

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

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

舉個例子,比如中序的時候,從以d為root的最下面子樹開始,沒有左子樹,所以d,e,之后遍歷以b為root的子樹,應該是b,f,但f也是root,所以這時候不到f,而是g,之后才f

中序,左中右的時候,如果該右節點了,右節點有子節點,先去子節點,但是前序就是,該誰就先寫上,之后再看他的子節點

leetcode-solution-java傳送門(github)(解析在文件夾下readme中):
postorder
preorder
inorder
my gist 代碼答案傳送門(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, 全局,遞歸開始狀態是當前狀態。但是全局變量有缺點,比如占內存;全局內可修改,易出錯

preorder下遞歸三要素:
1. 遞歸的定義:把root為根的proorder放入result
2. 遞歸結束的時候,該root的樹節點都加入了,當前為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中講的三種方法的實現

2. 復雜度分析

  1. tree problem time complexity:
    二叉樹通用時間復雜度計算公式 =(二叉樹的節點個數n * 每個節點的處理時間)
    比如divide conquer中都是if,處理時間就是O(1),總共就是O(n)
    notice!! only complete binary tree is logn, others , n/h
  2. 時間復雜度分析方法
    上篇回顧:
  3. binary search: 通過O(1)的時間,把規模為n的問題變為n/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)的時間,把規模為n的問題變為n/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)
*因為:1+2+4+8+…+n
=2+2+4+…+n-1
=2n
==>O(n)

本節兩個延伸問題:

  1. 通過O(n)的時間,把n的問題,變為了兩個n/2的問題,復雜度是多少? (比如: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)的時間,把n的問題,變成了兩個n/2的問題,復雜度是多少?
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)

這個問題也可以通過畫樹的方式來看,得到第一層樹的時候做了O(1)的工作,得到兩個n/2,下一步是做2 * O(1)的工作,得到4個n/4,... 最后還是(1+2+4+...+n) * O(1)得到最終結果O(1)

3. 解法

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

遞歸的基本思想是廣義地把規模大的問題轉化為規模小的相似的子問題或者相似的子問題集合來解決。廣義針對規模的,規模的縮小具體可以是指遞歸函數的參數,也可以是其參數之一。相似是指解決大問題的方法和解決小問題的方法往往是同一個方法,還可以是指解決子問題集的各子問題的方法是同一個方法。解決大問題的方法可以是由解決次規模問題的方法和解決剩余部分的方法組成,也可以是由一系列解決次規模問題的方法組成--http://zisong.me/post/suan-fa/ren-nao-li-jie-di-gui

4. 分類題目總結

  1. 比較簡單的例子,用divide-conquer和traverse都可以的:
  2. 題一:Binary Tree Paths:這個是兩種方法實現:兩種方法都是遞歸,所以都要先判斷root==null和左右節點==null的情況,做適合的返回
    1. divide-conquer: 類似于先找左右子樹的path,然后和root分別組成path,所以上面說的左右節點==null的情況要把當前節點加入,左右子樹的path也通過該方法獲得,最后把左子樹的所有path遍歷都加入root,右邊同理,得到path
    2. traverse:通常是站在root,向左右走,所以root先加入path,走到盡頭的路上一次加入節點,最后,左右節點==null的時候,要把當前已生成的path加入到結果集中。
//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:左邊取最大,右邊取最大,結果是這兩者之中的較大值+1(root),左右兩邊取最大就是遞歸調用自己,divide-conquer比較簡單
  2. 題三:Minimum Subtree:跟上面思路一樣,都取最小,區別在于上面是要返回最大值int的,這個要返回node,所以用一樣的分治法需要new class, Result{node; min; sum}來存儲我們需要的結果。 也可以用traverse+全局變量+divide-conquer的方法做
    貼一下這兩個題的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,或者說高度是想同的,三個條件
  3. 第一種解法看起來代碼短小精悍,很好,但這個解法最難的地方應該是maxdepth這個方法的返回值部分,最后如果遍歷到目前為止,是balanced的話,記錄當前的height就是通過return語句做的。應該是因為這里只要求返回Boolean,所以可以省個Result類
  4. Result類實現,兩種解法的思路其實就一樣,寫過第一種,第二種就可以自己寫出來了,需要注意一點的是當 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
    一個traverse方法,一個divide conquer,前者代碼更簡潔,后者思路更清晰,要注意一點是validate函數中如果沒有違背BST規則的話最后返回的是更新的Result,更新max用right.max, 更新min用left.min不要弄錯了
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的方法,和上面那個min subtree很類似,在用全局變量的基礎上加入了Result class,全局變量也有一個是Result 類型。一個小trick是求ave的時候用了 轉換成乘法的方式,避免除法帶來的誤差等各種問題
        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)
    比較簡單的一種traverse+divide conquer方法:一個全局變量longest和一個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系列:給兩個node A, B 找最近公共祖先
  2. 新手版,簡單的divide+conquer,因為這里不用管是不是都存在,所以后面那里直接返回left,right??雌饋硎欠N很取巧的方法
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兩點到root的path,然后從root開始一起遍歷兩條path,path上最后一個相同的點,就是LCA。這個思路很直接
        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>();
                //理解思路情況下這里寫錯了,判斷了parent并添加parent,不對。
                while(node!=null ){
                    path.add(node);
                    node = node.parent;
                }
                return path;
            }
3. 如果給的節點有一個不存在,返回null(新手版返回存在的那個節點)這個方法反正想不到,而且判斷是否存在那里,很繞。抄的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系列:給一個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.

思路是:
到每個點的時候,都幾下所有到這個點的path,包括不從頭出發的,只要是在當前點end的path,都存。比如1--2--4這樣的樹(請腦補成一棵樹),走到2, store[[],[2],[1,2]],同樣的走到4, store [[],[4],[1,2,4],[2,4]]. 其他見代碼注釋,也是個自己寫估計寫不出來的方法

public List<List<Integer>> binaryTreePathSum2(TreeNode root, int target) {
                List<List<Integer>> results = new ArrayList<List<Integer>>();
                //buffer這名字意味著這個東西是起輔助作用的,幫助記錄path,從而在得到target的時候可以在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;
                //因為后面還要傳入sum,所以copy一個sum值來操作而不在原數據上操作。這里對tmp sum的操作是-=,target-=nodes.vals直到等于0時,就說明這幾個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個節點的樹,任意兩點選路徑共有n choose 2條,可以暴力解,枚舉所有兩點之間有路徑的情況,以當前點分左右所有節點兩部分,兩部分兩兩配對)

可以拐彎的follow-up,關注點都在拐點,拐點前后,必是直上直下,所以可以用正常方法得到,之后在拼接(代碼自己并不能獨立寫,不貼了)

5. 樹和分治總結

二叉樹
給出一棵Binary Tree的字符串表示,比如[1,[2,3]],還原這棵二叉樹(高頻)
給出一棵Binary Tree的先序和中序遍歷序列,還原這棵二叉樹
給出一棵Binary Tree,按照深度(同樣深度從左往右)遍歷并輸出結果
給出一棵Binary Tree,輸出每一條從根節點到葉子節點的路徑
給出一棵Binary Tree,輸出與之鏡面對稱的二叉樹
給出兩棵Binary Tree,判斷這兩棵二叉樹是否完全一樣(形狀和每個點的value都要相同才算完全一樣)
給出兩棵Binary Tree,A和B,判斷B是否為A的子樹
分治法
求一棵二叉樹的最大深度(分治思想的簡單應用)
給出一棵Binary Tree,求出這棵二叉樹上和最大的路徑
給出一棵Binary Search Tree,問是否是Balanced Binary Search Tree
合并k個排好序的List(高頻)
求一個Array中的中位數(高頻,partition方法)
給出兩個排好序的List,輸出這兩個序列中的中位數,如果存在兩個中位數則輸出這兩個數的平均數
給出一個Array,求出Array中的每個元素的右邊比其小的元素個數(歸并排序應用)
給出一個平面上的若干個點,求其中最近的兩個點的距離(要求時間復雜度小于n^2)
給出一個n*n的棋盤,n是2的冪,開始時這個棋盤上只有一個格子是黑色的,其他均是白色的?,F在需要用一個黑色的L型去填滿這個棋盤,求一種填滿的方案(要求時間復雜度盡可能低)
Reference

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

6. 其他相關問題

? 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. 求二叉樹中的節點個數:
    getNodeNumRec(遞歸),getNodeNum(迭代)
  2. 求二叉樹的深度:
    getDepthRec(遞歸),getDepth
  3. 分層遍歷二叉樹(按層次從上往下,從左往右):
    levelTraversal, levelTraversalRec(遞歸解法)
  4. 將二叉查找樹變為有序的雙向鏈表:
    convertBST2DLLRec, convertBST2DLL

如果二叉查找樹不為空:
如果左子樹為空,對應雙向有序鏈表的第一個節點是根節點,左邊不需要其他操作;
如果左子樹不為空,轉換左子樹,二叉查找樹對應雙向有序鏈表的第一個節點就是左子樹轉換后雙向有序鏈表的第一個節點,同時將根節點和左子樹轉換后的雙向有序鏈 表的最后一個節點連接;
如果右子樹為空,對應雙向有序鏈表的最后一個節點是根節點,右邊不需要其他操作;
如果右子樹不為空,對應雙向有序鏈表的最后一個節點就是右子樹轉換后雙向有序鏈表的最后一個節點,同時將根節點和右子樹轉換后的雙向有序鏈表的第一個節點連 接。

  1. 求二叉樹第K層的節點個數:
    getNodeNumKthLevelRec, getNodeNumKthLevel

(1)如果二叉樹為空或者k<1返回0
(2)如果二叉樹不為空并且k==1,返回1
(3)如果二叉樹不為空且k>1,返回左子樹中k-1層的子節點個數與右子樹k-1層子節點個數之和(這個描述有點奇怪)

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. 求二叉樹中葉子節點的個數:
    (1)如果二叉樹為空,返回0
    (2)如果二叉樹不為空且左右子樹為空,返回1
    (3)如果二叉樹不為空,且左右子樹不同時為空,返回左子樹中葉子節點個數加上右子樹中葉子節點個數
  2. 判斷兩棵二叉樹是否相同的樹:
    (1)如果兩棵二叉樹都為空,返回真
    (2)如果兩棵二叉樹一棵為空,另一棵不為空,返回假
    (3)如果兩棵二叉樹都不為空,如果對應的左子樹和右子樹都同構返回真,其他返回假
  3. 求二叉樹的鏡像(破壞和不破壞原來的樹兩種情況):
    (1)如果二叉樹為空,返回空
    (2)如果二叉樹不為空,求左子樹和右子樹的鏡像,然后交換左子樹和右子樹
    判斷兩個樹是否互相鏡像
  4. 求二叉樹中節點的最大距離:
    (1)如果二叉樹為空,返回0,同時記錄左子樹和右子樹的深度,都為0
    (2)如果二叉樹不為空,最大距離要么是左子樹中的最大距離,要么是右子樹中的最大距離,要么是左子樹節點中到根節點的最大距離+右子樹節點中到根節點的最大距離,同時記錄左子樹和右子樹節點中到根節點的最大距離。
  5. 由前序遍歷序列和中序遍歷序列重建二叉樹:
    rebuildBinaryTreeRec
  6. 判斷二叉樹是不是完全二叉樹:
    若設二叉樹的深度為h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第 h 層所有的結點都連續集中在最左邊,這就是完全
    二叉樹。
    有如下算法,按層次(從上到下,從左到右)遍歷二叉樹,當遇到一個節點的左子樹為空時,則該節點右子樹必須為空,且后面遍歷的節點左
    右子樹都必須為空,否則不是完全二叉樹。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,702評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,143評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,553評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,416評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,940評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,170評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,709評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,597評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,029評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,403評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,746評論 2 370

推薦閱讀更多精彩內容