二叉树算法汇总

二叉树是面试必考题目,这里总结一些常见的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

 

原文地址:https://www.cnblogs.com/ooon/p/5579551.html