zoukankan      html  css  js  c++  java
  • 二叉树算法汇总

    二叉树是面试必考题目,这里总结一些常见的2叉树的题目,树的定义如下:

     public class TreeNode {
          int val;
          TreeNode left;
          TreeNode right;
          TreeNode(int x) { val = x; }
     }
    View Code

    1 统计树的深度

    非递归版本可以层次遍历,遍历是记录深度;

    递归版本如下:

     public int minDepth(TreeNode root) {
            if(root == null)  return 0;
            return dfs(root);
        }
        public int dfs(TreeNode root){
          if( root == null ) return 0;
          int ldep = dfs(root.left);
          int rdep = dfs(root.right);
          return (ldep > rdep ? ldep : rdep) + 1;
        }
    View Code

    变形1)判断左子树比右子树高1,则先求左子树高,再求右子树高,判断差值是否为1即可

      2)求树的最小高度,比如下图为 3 ,这个也简单的修改下即可,在双分叉节点上返回最小的高度,在单分叉或没分叉的节点上返回最大的高度;

          1

        /  

             2      3

           /       /  

         4       5     6

                       /

                     7 

    2 统计根节点到叶节点的路径的和( 129. Sum Root to Leaf Numbers)

        public int sumNumbers(TreeNode root) {
            return dfs(root,0);
        }
        public int dfs(TreeNode p,int s){
            if(p ==null ){
                return 0;
            }
            if(p.left == null && p.right == null){
                return s*10 + p.val;
            }
            return dfs(p.left, s*10 +p.val) + dfs(p.right, s*10 +p.val);
        }
    View Code

     3.统计叶节点的个数

      //暂时先pass

      pass

    4.最近公共祖先LCA(Lowest Common Ancestor of a Binary Tree)

     1)找两条到 P Q 的路径,然后找到这两条路径中最后一个相同的元素即可,注意对于BST,查找可利用BST有序的性质,对于P Q 分别做一个查找,得到路径,不必一起查找。

     
        List<TreeNode> p_path = null;
        List<TreeNode> q_path = null;
        List<TreeNode> path   = null;
        public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
            p_path = new LinkedList<TreeNode>();
            q_path = new LinkedList<TreeNode>();
            path   = new LinkedList<TreeNode>();
            dfs(root,p,q,path,p_path,q_path);
            TreeNode res = root;
            int idx = 0 ;
            //找到两个列表中最后一个相同元素的方法
            while(idx < p_path.size() && idx < q_path.size()){
                if(p_path.get(idx) != q_path.get(idx)) break;
                else res = q_path.get(idx++); 
            }
            return res;
        }
        
        public void dfs(TreeNode root, TreeNode p, TreeNode q, 
            List<TreeNode> path, List<TreeNode> p_path, List<TreeNode> q_path ){
            //注意找某节点路径的方法,什么时候回溯,什么时候更改
            if(root != null){
                path.add(root);
                if(root == p) p_path.addAll(path);
                if(root == q) q_path.addAll(path);
                if(p_path .size() > 0 && q_path .size() > 0) return ;
                dfs(root.left , p, q, path, p_path, q_path);
                dfs(root.right, p, q, path, p_path, q_path);
                path.remove(path.size()-1);
            }    
        }
    View Code

     2)如果P或者Q本身不是祖先的话,节点P与节点Q的公共祖先R一定满足:P与Q分别出现在R的左右子树上,而如果P Q 其中一个是公共祖先,则返回第一个找到的节点即可。

    如果当前子树既包含P,又包含Q,则当前节点为 LCA,如果只包含其中的一个则直接返回那一个,否则返回空。

    //1)如果P或者Q本身不是祖先的话,节点P与节点Q的公共祖先R一定满足:P与Q分别出现在R的左右子树上.
        //2)如果P Q 其中一个是公共祖先,则返回第一个找到的节点。
        public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
            if( root ==null ) return null;
            if(root == p || root == q) return root;
            TreeNode left  = lowestCommonAncestor(root.left ,p,q);
            TreeNode right = lowestCommonAncestor(root.right,p,q);
            if(left != null && right !=null) return root;
            return left == null ? right : left;
        }
    View Code

      3)对于BST 方法是这样的 根据树的有序性,所以-只需找到第一个满足 P<=R<=Q 或者 P>=R>=Q的节点皆可

     public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
             TreeNode r = root;
             while(r != null){
                 if(p.val < r.val &&  q.val < r.val) r= r.left;
                 else if(p.val > r.val &&  q.val > r.val) r = r.right;
                 else break;
             }
             return r;
        }
    View Code

    5. leetcode : 117. Populating Next Right Pointers in Each Node II  

    /**
     * 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 ;
            root.next = null;
            dfs(root);
        }
        public void link(TreeLinkNode tail ,TreeLinkNode p){
                if(tail.next != null) return;
                while(p != null){
                    if(p.left !=null && p.right != null) {
                        tail.next = p.left ; tail = tail.next;
                        tail.next = p.right; tail = tail.next;
                    }else if(p.left != null){tail.next = p.left ; tail = tail.next;}
                    else if(p.right!= null){tail.next = p.right; tail = tail.next;}
                    p = p.next;
                }
        }
        //注意每次遍历,用 link 方法将 root 的下一层的节点整体串起来
        public void dfs(TreeLinkNode root){
            if(root == null) return;
            if(root.left == null && root.right == null) return;
            if(root.left == null){//左空
                if(root.next == null) root.right.next = null;
                else link(root.right,root.next);
            }else if(root.right == null){//右空
                if(root.next == null) root.left.next = null;
                else link(root.left,root.next);
            }else{  //左右均不空
                root.left.next = root.right;
                if(root.next == null) root.right.next = null;
                else link(root.right,root.next);
            }
            if(root.left !=null) dfs(root.left );
            if(root.right!=null) dfs(root.right);
        }
    }
    View Code

     6. 层次遍历

      这里直接给出层次遍历的分层打印方法,用parent 与 children 两个变量分别统计当前层与下一层的节点数,每次拿出一个节点,当前层计数 parent-1;当parent == 0;输出一个换行符,然后另 parent = children ,children =0 即可。这个题对 5)有效。

      public void lfs(Node root) {
               if(root == null) return ;
               //十分万能的,当队列或者栈都可以用的 LinkedList。
               LinkedList<Node> queue = new LinkedList<Node>();
               queue.add(root);
               int parent = 1;
               int children = 0;
               while(!queue.isEmpty()){
                    System.out.print(queue.peek() +" ");
                    Node cur = queue.removeFirst();
                    --parent; 
                    if(cur.left  != null) {queue.addLast(cur.left ); ++children;}
                    if(cur.right != null) {queue.addLast(cur.right); ++children;}
                    if(parent == 0) {System.out.println(); parent = children; children = 0;}
               }
            }
    View Code

      dfs 模拟 bfs:

    //dfs 模拟 bfs
        public List<List<Integer>> levelOrder(TreeNode root) {
            List<List<Integer>> res= new ArrayList<List<Integer>>();
            if(root == null) return res;
            dfs(root,0,res);
            return res;
        }
        public void dfs(TreeNode root, int height, List<List<Integer>> res){
            if(root == null) return;
            if(res.size() <= height){
                res.add(new ArrayList<Integer>());
            }
            res.get(height).add(root.val);
            dfs(root.left , height+1, res);
            dfs(root.right, height+1, res);
        }
    View Code

    7. 判断一棵二叉树是否按照中轴对称

    public boolean isSymmetric(TreeNode root) {
              if(root == null) return true;
              return dfs(root.left,root.right);
        }
        public boolean dfs(TreeNode left,TreeNode right){
            if(left == null || right == null) return left == right;
            if(left.val != right.val) return false;
            return dfs(left.left,right.right) && dfs(left.right,right.left);
        }
    View Code

     8. 二叉树非递归遍历

    先序与中序非常的 easy,直接给代码

    //pre-order
     public List<Integer> preorderTraversal(TreeNode root) {
            List<Integer> res = new  ArrayList<Integer>();
            if(root == null) return res;
            LinkedList<TreeNode> s = new  LinkedList<TreeNode>();
            while(root != null || !s.isEmpty()){
               while(root!=null){
                   s.push(root);
                   res.add(root.val);
                   root = root.left;
               }
               if(!s.isEmpty()){
                   root = s.pop();
                   root = root.right;
               }
            }
            return res;
        }    
    //in-order
    public List<Integer> inorderTraversal(TreeNode root) {
            List<Integer> res = new ArrayList<Integer>();        //结果集
            LinkedList<TreeNode> s = new LinkedList<TreeNode>(); //辅助栈
            if(root == null) return res;
            while(root != null || !s.isEmpty()){
               while(root != null){
                   s.push(root);
                   root = root.left;
               }//此时 root 已经为空了
               if(!s.isEmpty()){
                   root = s.pop();
                   res.add(root.val);
                   root = root.right;
               }
            }
            return res;
        }
    View Code

    后序比较困难,这里给三种方式:

    法1:pre-order : root-left-right ,post-order 需要 left-right-root,所以在pre-order的时候先放right再放left得到 root-right-left ,然后反转即可,非常 tricky 的idea.

    public List<Integer> postorderTraversal(TreeNode root) {
            LinkedList<Integer> res = new LinkedList<Integer>();
            LinkedList<TreeNode> s = new LinkedList<TreeNode>();
            if(root == null) return res;
            while(root != null || !s.isEmpty()){
                while(root != null){
                    s.push(root);
                    res.add(root.val);
                    //不同于 preOrder 这里先右
                    root = root.right; 
                }
                if(!s.isEmpty()){
                    root = s.pop();
                    root = root.left;
                }
            }
            Collections.reverse(res);
            return res;//翻转再返回结果就好
     }
    View Code

    法2:双栈法,其实就是利用两个栈构造出 root-right-left 的序列,然后出栈即为反转了,不如上一个快

    给两个栈 s1,s2;

    将root入s1;

    若s1非空,将s1的栈顶元素赋给root,然后将root入s2;然后先将root左结点入s1,后将root右结点入s1,顺序不能错;

    若s2非空,出s2,就获得后序遍历;

    public List<Integer> postorderTraversal(TreeNode root) {
            LinkedList<Integer> res = new LinkedList<Integer>();
            //两个 stack  ,一个 stack按跟左右进栈
            //放到另一个stack,形成 跟 右左,然后出栈即得到结果
            LinkedList<TreeNode> s1 = new LinkedList<TreeNode>();
            LinkedList<TreeNode> s2 = new LinkedList<TreeNode>();
            if(root == null) return res;
            s1.push(root);
            while(!s1.isEmpty()){
                root = s1.pop();
                s2.push(root);
                if(root.left != null) 
                    s1.push(root.left);
                if(root.right!= null) 
                    s1.push(root.right);
            }
            while(!s2.isEmpty())
                res.add(s2.pop().val);
            return res;
    }
    View Code

    法3:直接法:

    root表示下一个要处理的结点,初始化为根结点,pre 表示上一次刚刚输出过的结点
    pre 的作用是对有孩子的结点进行判断,由于算法特性,每次到某个结点时,其左子树都输出过了

    此时只要判断 pre 是否是当前结点的右孩子,如果不是,那么说明其右子树没走过,那么 root = 当前结点的右子树

    否则就是刚刚输出了当前结点的右孩子,由于是后序,其右子树也必定都输出过了,此时只要输出当前结点,更新 pre 就好了。

    public List<Integer> postorderTraversal(TreeNode root) {
            LinkedList<Integer> res = new LinkedList<Integer>();
            LinkedList<TreeNode> s = new LinkedList<TreeNode>();
            if(root == null) return res;
            TreeNode pre = null; //指向上一个访问的节点
            while(root != null || !s.isEmpty()){
                while(root != null){
                    s.push(root);
                    root = root.left;
                }
                if(!s.isEmpty()){ // 此时栈顶元素的左子树都输出了
                    //左右子树都输出了,当前节点也可以输出
                    if(s.peek().right == null || s.peek().right == pre){
                        res.add(s.peek().val);
                        pre = s.pop();
                    }else{//右子树存在,且没有入栈
                        root = s.peek().right;
                    }
                }
            }
            return res;
    }
    View Code

     9.根据先序序列、中序序列来建立二叉树

      直接递归即可,注意下标的范围要计算好

    public TreeNode build(int[] pre, int plo, int phi, int[]in, int ilo, int ihi){
            if(plo > phi) return null;
            TreeNode root = new TreeNode(pre[plo]);
            int pos = ilo;//不用 binary search,因为不移动有序
            while(pos <= ihi){
              if(in[pos] == root.val) break;
              else pos ++;
            } 
            //这里的下标要严格注意
            root.left = build(pre, plo+1, plo+(pos-ilo), in, ilo, pos-1);
            root.right= build(pre, phi-(ihi-pos-1), phi, in, pos+1, ihi);
            return root;
        }
    View Code

    10. 有序链表转化为平衡二叉树

      类似于上边的递归,注意判别条件,左子树 cur == head 说明左边已经遍历完

    public TreeNode sortedListToBST(ListNode head) {
            if(head == null) return null;
            ListNode runner = head;// runner 为快指针
            ListNode walker = head;// walker 为慢指针
            ListNode pre = null;   // pre 为 walker 的前一个
            //快慢指针法的判别条件
            while(runner.next!=null&&runner.next.next!=null){
                runner = runner.next.next;
                pre = walker;
                walker = walker.next;
            }
            
            if(pre != null ) pre.next = null; //切断链表
            TreeNode root = new TreeNode(walker.val);
            //pre == head 意味着 左子树的最后一个节点以建好,不用在建
            if(walker != head) root.left = sortedListToBST(head);
            root.right = sortedListToBST(walker.next);//第一句判定错误
            return root;
        }
    View Code

     11.统计完全树节点的个数

      简单的方法是直接统计左右子树高度,若相等,则说明以该节点为跟的子树是满二叉树,直接返回 2h-1 即可,若不是,则返回 count(左子树)+count(右子树)+1:

    public int countNodes(TreeNode root) {
            if(root == null) return 0;
            TreeNode p = root, q = root;
            int hl = 0, hr = 0;
            while(p != null){//统计左子树高
                 p = p.left;
                 hl ++;
            }
            while(q != null){//统计右子树高
                 q = q.right;
                 hr ++;
            }
            if(hl == hr) return (1 << hl) - 1;// <=> 2^hl - 1
            return 1 + countNodes(root.left) + countNodes(root.right);
        }
    View Code

     12.递归建立 BST

    static TreeNode root = null;
        public static void build(int[] nums)
        {
            for(int i=0; i<nums.length; ++ i)
            {
                build(root, nums[i]);
            }
        }
        
        public static void build(TreeNode node, int n) {
            if (root == null) 
            {
                root = new TreeNode(n);
                return;
            }
            if (n < node.val) { //落在左子树
                if (node.left == null)
                    node.left = new TreeNode(n);
                else
                    build(node.left, n);
    
            } else { // 落在右子树
                if (node.right == null)
                    node.right = new TreeNode(n);
                else
                    build(node.right, n);
            }
        }
    View Code

     13.判断一棵树是否是另一颗树的子结构

    public boolean HasSubtree(TreeNode root1, TreeNode root2) {
    
            if (root1 == null || root2 == null) return false;
    
            return judge(root1, root2) 
                    || judge(root1.left, root2) || judge(root1.right, root2);
    
        }
    
        public boolean judge(TreeNode root1, TreeNode root2) {
    
            if (root2 == null) return true;
            if (root1 == null) return false;
    
            return root1.val == root2.val 
                    && judge(root1.left, root2.left) && judge(root1.right, root2.right);
        }
    View Code

     14. 将 BST 每个节点的值替换为比其大的值的和。

    // 将二叉查找树替换为其中序遍历后边节点的和
        int sum = 0;
        public void Rep(TreeNode root)
        {
            if(root == null) return;
            Rep(root.right);
            sum += root.val;
            root.val = sum - root.val;
            Rep(root.left );
        }
    View Code

     

  • 相关阅读:
    移动网页 ----仿淘宝使用flex布局实现页面 固定顶部和底部
    微信小程序 模板template的使用
    JQ 移动端返回顶部,往下滑动时显示返回按钮,往上滑动时隐藏返回按钮
    JQ模态框+简易评价
    JQ多行文本溢出省略号插件
    评论框字符串判断
    jQuery+ajax+本地josn文件数据 测试
    vue 移动端环境搭建
    移动端前端适配方案20170707
    城通网盘,千军万马,千脑网盘,119g网盘哪个适合做网赚?
  • 原文地址:https://www.cnblogs.com/ooon/p/5579551.html
Copyright © 2011-2022 走看看