算法笔记:树和分治+复杂度分析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。看起来是种很取巧的方法
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的幂,开始时这个棋盘上只有一个格子是黑色的,其他均是白色的。现在需要用一个黑色的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阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容