二叉树算法总结

本文详细总结了二叉树的各种遍历方法(递归与非递归)、层次遍历、路径问题、深度计算、镜像操作、最低公共祖先、子结构判断、平衡与对称性检查、序列化、二叉搜索树转换等经典算法问题,涵盖了《剑指Offer》的相关题目,全部实现并已通过验证。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

二叉树的递归遍历(先序,中序,后序)AC
二叉树的非递归遍历(先序,中序,后序)AC
二叉树的层次遍历AC

二叉树的之字形遍历AC
二叉树的先序遍历、中序遍历重建二叉树AC

二叉树中和为某一值的路径(路径问题)AC

求二叉树的深度AC
求二叉树的镜像AC
求二叉树的最低公共祖先AC

判断二叉树是否为二叉树的子结构AC

判断二叉树是否为平衡二叉树AC
判断二叉树是否为对称二叉树AC

序列化二叉树AC

二叉搜索树转为双向链表AC

二叉搜索树的第k个节点AC
判断是否为二叉搜索树的后序遍历序列AC

1.二叉树的递归遍历(先序遍历、中序遍历、后序遍历)AC

package tree;


import java.util.*;


/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
    public TreeNode(int val) {
        this.val = val;
    }
}*/
//nowcoder pass
public class TreeToSequence {   


    //先序遍历AC
    public void preOrder(TreeNode root, List<TreeNode> list) {
        if (root != null) {
            list.add(root);
            preOrder(root.left, list);
            preOrder(root.right, list);
        }
    }
    
    //中序遍历AC
    public void inOrder(TreeNode root, List<TreeNode> list) {
        if (root != null) {
            inOrder(root.left, list);
            list.add(root);
            inOrder(root.right, list);
        }
    }
    
    //后序遍历AC
    public void afterOrder(TreeNode root, List<TreeNode> list) {
        if (root != null) {
            afterOrder(root.left, list);
            afterOrder(root.right, list);
            list.add(root);
        }
    }
}

2.二叉树的非递归遍历(先序遍历、中序遍历、后序遍历)AC

package tree;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;


//二叉树的非递归遍历
//nowcoder ac
public class TreeToSequence {
    
    //先序遍历:非递归(AC)
    public List<TreeNode> preOrder(TreeNode root) {
        if (root == null) {
            return null;
        }
        
        List<TreeNode> list = new ArrayList<TreeNode>();
        Stack<TreeNode> stack = new Stack<TreeNode>();
        stack.push(root);       
        while (!stack.isEmpty()) {
            TreeNode node = stack.pop();
            list.add(node);
            if (node.right != null) {
                stack.push(node.right);
            }
            if (node.left != null) {
                stack.push(node.left);
            }
        }
        
        return list;
    }
   
    //中序遍历:非递归(AC)(此方法太难记了,可忽略,见下面的【二叉树的非递归中序遍历】)
    public List<TreeNode> inOrder(TreeNode root) {
        if (root == null) {
            return null;
        }
        
        List<TreeNode> list = new ArrayList<TreeNode>();
        Stack<TreeNode> stack = new Stack<TreeNode>();
        TreeNode temp = root;
        //根入栈
        stack.push(root);
        while (!stack.isEmpty()) {
            while (temp != null) {
                temp = temp.left;
                if (temp != null) {
                    stack.push(temp);
                }
            }
            //左:左为null
            //根:弹出根,访问
            temp = stack.pop();
            list.add(temp);
            //右:将右入栈,作为根,进入下一次大循环
            temp = temp.right;
            if (temp != null) {
                stack.push(temp);
            }
        }
        return list;
    }
    
    //后序遍历:非递归(AC)
    //后序遍历的双栈实现
    //其实就是改写了先序遍历,将根左右改为了根右左,然后利用栈,将顺序变为了左右根,即为后序遍历
    public List<TreeNode> afterOrder(TreeNode root) {
        if (root == null) {
            return null;
        }
        
        List<TreeNode> list = new ArrayList<TreeNode>();
        Stack<TreeNode> stack = new Stack<TreeNode>();
        Stack<TreeNode> stack2 = new Stack<TreeNode>();
        stack.push(root);       
        while (!stack.isEmpty()) {
            TreeNode node = stack.pop();
            stack2.push(node);
            if (node.left != null) {
                stack.push(node.left);
            }
            if (node.right != null) {
                stack.push(node.right);
            }
        }
        
        while (!stack2.isEmpty()) {
            list.add(stack2.pop());
        }
        return list;
    }
}

3.二叉树的非递归中序遍历AC

package tree;

import java.util.ArrayList;
import java.util.Stack;
/**
二叉树的非递归中序遍历
中序遍历:左根右
思想:
root存入temp,temp为当前节点访问节点
1.一直往左去,左孩子一直进栈,直到左孩子为null
2.栈顶元素出栈,访问。
3.右孩子为当前节点访问节点,回到1


如何理解以上过程为中序遍历(左根右)
root存入temp,temp为当前节点访问节点
1.一直往左去,左孩子一直进栈,直到左孩子为null(当左孩子为null,相当于访问了【左】孩子)
2.栈顶元素出栈,访问。(访问【根】)
3.右孩子为当前节点访问节点,回到1(【右】孩子做根,继续左根右的过程)
 */
public class InOrder {
    //nowcoder pass
    public ArrayList<Integer> inorderTraversal(TreeNode root) {
        ArrayList<Integer> ret = new ArrayList<>();
        if (root == null) {
            return ret;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode temp = root;
        while (temp != null || !stack.isEmpty()) {//是或
            while (temp != null) {
                stack.push(temp);
                temp = temp.left;
            }
            temp = stack.pop();
            ret.add(temp.val);
            temp = temp.right;
        }
        return ret;
    }
    
//  public ArrayList<Integer> inorderTraversal(TreeNode root) {
//      ArrayList<Integer> ret = new ArrayList<>();
//      if (root == null) {
//          return ret;
//      }
//      Stack<TreeNode> stack = new Stack<>();
//      stack.push(root);
//      TreeNode temp = root;
//      while (!stack.isEmpty()) {
//          while (temp != null) {
//              if (temp.left != null) {
//                  stack.push(temp.left);
//              }
//              temp = temp.left;
//          }
//          // 左访问完了
//          // 根
//          temp = stack.pop();
//          ret.add(temp.val);
//          temp = temp.right;
//          if (temp != null) {
//              stack.push(temp);
//          }
//      }
//      return ret;
//  }
}

4.二叉树的层次遍历AC

《剑指Offer》题目23

package tree;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;

//nowcoder pass
public class LevelPrint {

    public ArrayList<ArrayList<Integer> > Print(TreeNode root) {
        ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
        if (root == null) {
            return ret;
        }
        ArrayList<Integer> list = new ArrayList<Integer>();
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.offer(root);
        queue.offer(null);//层分隔符
        while (queue.size() != 1) {
            TreeNode node = queue.poll();
            if (node == null) {
                queue.offer(null);
                ret.add(new ArrayList<Integer>(list));
                list.clear();
                continue;
            } 
            list.add(node.val); 
            if (node.left != null) {
                queue.offer(node.left);
            }
            if (node.right != null) {
                queue.offer(node.right);
            }
        }
        ret.add(new ArrayList<Integer>(list));
        return ret;
    }
}

5.二叉树的层次遍历之字形打印AC

《剑指Offer》题目61

package tree;

import java.util.*;

/**
按之字形顺序打印二叉树

题目描述
请实现一个函数按照之字形打印二叉树,
即第一行按照从左到右的顺序打印,
第二层按照从右至左的顺序打印,
第三行按照从左到右的顺序打印,其他行以此类推
 */
//nowcoder pass
public class Solution8 {
    public ArrayList<ArrayList<Integer> > Print(TreeNode root) {
        ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
        if (root == null) {
            return ret;
        }
        ArrayList<Integer> list = new ArrayList<>();
        Deque<TreeNode> deque = new LinkedList<>();
        deque.offer(null);//层分隔符
        deque.offer(root);
        boolean leftToRight = true;
        while (deque.size() != 1) {
            TreeNode node = deque.poll();
            if (node == null) {//到达层分隔符
                if (leftToRight) {
                    Iterator<TreeNode> iterator = deque.iterator();
                    while (iterator.hasNext()) {
                        list.add(iterator.next().val);
                    }
                } else {
                    Iterator<TreeNode> iterator = deque.descendingIterator();
                    while (iterator.hasNext()) {
                        list.add(iterator.next().val);
                    }
                }
                leftToRight = !leftToRight;
                ret.add(list);
                list = new ArrayList<Integer>();
                deque.offer(null);
                continue;
            }
            if (node.left != null) {
                deque.offer(node.left);
            }
            if (node.right != null) {
                deque.offer(node.right);
            }
        }
        return ret;
    }
}

6.二叉树的先序遍历和中序遍历重建二叉树(递归实现)AC

《剑指Offer》题目6

package tree;

//nowcoder pass
public class ReConstructBinaryTree {

    public TreeNode reConstructBinaryTree(int[] preOrder, int[] inOrder) {
        if (preOrder == null || inOrder == null) {
            return null;
        }
        if (preOrder.length != inOrder.length) {
            return null;
        }
        
        return constructCore(preOrder, 0, preOrder.length - 1, inOrder, 0, inOrder.length - 1); 
    }
        
    public TreeNode constructCore(int[] preOrder, int preStart, int preEnd,
            int[] inOrder, int inStart, int inEnd) {
        //前序遍历的第一个是根结点
        int rootValue = preOrder[preStart];
        TreeNode root = new TreeNode(rootValue);
        root.left = null;
        root.right = null;
        
        //在中序遍历中寻找根节点的位置
        int inRootIndex = Integer.MIN_VALUE;
        for (int i = inStart; i <= inEnd; i++) {
            if (inOrder[i] == rootValue) {
                inRootIndex = i;
                break;
            }
        }
            
        int leftCount = inRootIndex - inStart;
        int rightCount = inEnd - inRootIndex;
        
        //构建左子树
        if (leftCount > 0) {
            int leftPreStart = preStart + 1;
            int leftPreEnd = leftPreStart + leftCount - 1;
            int leftInStart = inStart;
            int leftInEnd = leftInStart + leftCount - 1;
            root.left = constructCore(preOrder, leftPreStart, leftPreEnd, inOrder, leftInStart, leftInEnd);
        }
        //构建右子树
        if (rightCount > 0) {
            int rightPreStart = preEnd - rightCount + 1;
            int rightPreEnd = preEnd;
            int rightInStart = inEnd - rightCount + 1;
            int rightInEnd = inEnd;
            root.right = constructCore(preOrder, rightPreStart, rightPreEnd, inOrder, rightInStart, rightInEnd);
        }
        return root;
    }
}

7.二叉数中和为某一值的路径AC

《剑指Offer》题目25

package tree;


public class TreePathSummary {

//  路径问题
//  findPath(root) {
//      path.push(root);
//      
//      findPath(root.left);
//      findPath(root.right);
//      
//      path.pop();
//  }
}


package tree;


import java.util.ArrayList;


/**
*题目:
*输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。
*路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
*    10
*    /\
*   5  12
*  / \
* 4   7
* 路径和为22的为10-12,10-5-7
*/


//nowcoder pass
public class TreePathSum {


   private ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
   private ArrayList<Integer> list = new ArrayList<Integer>();
   
   public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
       if (root == null) {
           return ret;
       }
       
       findPathCore(root, target);
       return ret;
   }
   
   public void findPathCore(TreeNode root, int target) {
       list.add(root.val);
       
       if (target - root.val == 0 && root.left == null && root.right == null) {
           ret.add(new ArrayList<>(list));
       }
       
       if (root.left != null) {
           findPathCore(root.left, target - root.val);
       }
       if (root.right != null) {
           findPathCore(root.right, target - root.val);
       }
       
       list.remove(list.size() - 1);
   }
}

8.求二叉树的深度AC

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
 
    public TreeNode(int val) {
        this.val = val;
 
    }
 
}
*/
//nowcoder pass
public class Solution {
    public int TreeDepth(TreeNode root) {
                if (root == null) {
            return 0;
        }
         
        int leftDepth = TreeDepth(root.left);
        int rightDepth = TreeDepth(root.right);
         
        return Math.max(leftDepth, rightDepth) + 1;
    }
}

9.求二叉树的镜像AC

《剑指Offer》题目19

package tree;
/**
 题目:
操作给定的二叉树,将其变换为源二叉树的镜像。 
输入描述:
二叉树的镜像定义:源二叉树 
            8
           /  \
          6   10
         / \  / \
        5  7 9 11
        镜像二叉树
            8
           /  \
          10   6
         / \  / \
        11 9 7  5
 */


//nocoder pass
public class MirrorTree {


    public void Mirror(TreeNode root) {
        if (root != null) {
            TreeNode temp = root.left;
            root.left = root.right;
            root.right = temp;
            
            Mirror(root.left);
            Mirror(root.right);
        }
    }
}

10.求二叉树中两个节点的最低公共祖先AC

《剑指Offer》题目50

package tree;
/**
二叉树的最低公共祖先


题目描述:
给定一棵树,同时给出树中的两个结点(n1和n2),求它们的最低公共祖先
思路:
递归
 */
//leetcode pass
public class Solution {
    
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null) {
            return null;
        }
        if (root == q) {
            return q;
        }
        if (root == p) {
            return p;
        }
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        //如果都不空,说明p,q一个在左子树,一个在右子树,最低公共祖先为root
        if (left != null && right != null) {
            return root;
        }
        return left != null ? left : right;
    }
}

11.判断二叉树是否为二叉树的子结构AC

注意是二叉树的子结构,不是二叉树的子树
《剑指Offer》题目18

package tree;

//nowcoder pass
public class SubTree {


    public boolean HasSubtree(TreeNode root, TreeNode tree) {
        boolean result = false;
        
        if (root != null && tree != null) {
            if (root.val == tree.val) {
                result = tree1HasTree2(root, tree);
            } 
            if (!result) {
                result = HasSubtree(root.left, tree) || HasSubtree(root.right, tree);
            }
        }
        return result;
    }
    //判断树是不是完全相同的
    public boolean tree1HasTree2(TreeNode root1, TreeNode root2) {
        if (root2 == null) {
            return true;
        }
        if (root1 == null) {
            return false;
        }
        
        if (root1.val != root2.val) {
            return false;
        }
        return tree1HasTree2(root1.left, root2.left) && tree1HasTree2(root1.right, root2.right);
    }
}

12.判断二叉树是否是平衡二叉树AC

《剑指Offer》题目39

package tree;
/**
判断是否是平衡二叉树


题目描述
输入一棵二叉树,判断该二叉树是否是平衡二叉树。
 *
 */
//nowcoder pass
public class Solution6 {
    
    //比较好的方法
    //先判断左右子树是否为平衡二叉树,在判断当前
    //需要保存是否为平衡二叉树和高度
    public boolean IsBalanced_Solution(TreeNode root) {
        int[] depth = new int[1];
        return isBalanced(root, depth);
    }
    
    public boolean isBalanced(TreeNode root, int[] depth) {
        if (root == null) {
            depth[0] = 0;
            return true;
        }
        
        int[] leftDepth = new int[1];
        boolean leftIsBalanced = isBalanced(root.left, leftDepth);


        int[] rightDepth = new int[1];
        boolean rightIsBalanced = isBalanced(root.right, rightDepth);
        
        depth[0] = Math.max(leftDepth[0], rightDepth[0]) + 1;
        if (leftIsBalanced && rightIsBalanced && Math.abs(leftDepth[0]-rightDepth[0])<=1) {
            return true;
        }
        return false;
    }
    
    
    //不好的方法,含有大量的重复操作
//  //对每个节点为root的树判断是否是平衡二叉树是按照根左右的顺序,
    //但是在根中已经有了对左右子树的判断,相当于对左右子树做了重复的判断
//  public boolean IsBalanced_Solution(TreeNode root) {
//      if (root == null) {
//          return true;
//      }
//      
//      int leftDepth = depth(root.left);
//      int rightDepth = depth(root.right);
//      if (Math.abs(leftDepth - rightDepth) >1) {
//          return false;
//      }
//      return IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right);
//    }
//    
//    public int depth(TreeNode root) {
//      if (root == null) {
//          return 0;
//      }
//      
//      int leftDepth = depth(root.left);
//      int rightDepth = depth(root.right);
//      
//      return Math.max(leftDepth, rightDepth) + 1;
//    }
}

13.判断二叉树是否为对称二叉树AC

《剑指Offer》题目59

package tree;
/**
 * 题目:
 * 请实现一个函数,用来判断一颗二叉树是不是对称的。
 * 注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的 
 *
 */

//nowcoder pass
public class Symmetrical {
    
    public boolean isSymmetrical(TreeNode root)
    {
       if (root == null) {
           return true;
       }
       return isSymmetricalCore(root.left, root.right);
    }
    
    public boolean isSymmetricalCore(TreeNode root1, TreeNode root2) {
        if (root1 == null && root2 == null) {
            return true;
        }
        if (root1 != null && root2 != null && root1.val == root2.val) {
            return isSymmetricalCore(root1.left, root2.right) && isSymmetricalCore(root1.right, root2.left);
        }
        return false;
    }
}

14.二叉树的序列化AC

《剑指Offer》题目62

package tree;
/**
序列化二叉树


题目描述
请实现两个函数,分别用来序列化和反序列化二叉树
牛客BAT算法课中讲过思路。
 */
//nowcoder pass
public class Solution9 {
   
    public String Serialize(TreeNode root) {
        if (root == null) {
            return "#!";
        }
        StringBuilder sb = new StringBuilder(root.val+"!");
        sb.append(Serialize(root.left));
        sb.append(Serialize(root.right));
        return sb.toString();
    }


    private int index = -1;
    public TreeNode Deserialize(String str) {
        index++;
        int len = str.length();
        if (index >= len) {
            return null;
        }
        String[] strArr = str.split("!");
        if (strArr[index].equals("#")) {
            return null;
        }
        TreeNode node = new TreeNode(Integer.parseInt(strArr[index]));
        node.left = Deserialize(str);
        node.right = Deserialize(str);
        return node;
    }
}

15.二叉搜索树转为双向链表AC

《剑指Offer》题目27
答案:

package tree;
/**
 * 理解递归!!!!:
 * 
 * f()
 * {
 *      //递归终止
 *      if ()
        {
            return;
        }
        //递归体
        ......
        //递归
 *      f();
 * }
 * 递归是什么?函数f调用f,注意f实现的功能是一样的!
 * 在本题中,即f的功能为将二叉搜索树转换为双向链表,并返回链表的头结点.
 * 递归处理
 * {
 *      //处理根
 * 
 *      //递归处理左子树
 *      //处理完后返回的即是左子树转换成的双向链表的头结点了!因此要尾结点与root相连!
 *      
 *      //递归处理右子树
 *      //处理完后返回的即是右子树转换成的双向链表的头结点了!因此要头结点与root相连!
 *      
 *      //返回转换后的双向链表的头结点
 * }处理根
 *
 */
//nowcoder pass
public class P27_二叉搜索树转为双向链表 {


    TreeNode Convert(TreeNode root) {
        if (root == null) {
            return null;
        }
        //对左子树进行变换
        TreeNode leftHead = Convert(root.left);
        if (leftHead != null) {
            TreeNode temp = leftHead;
            while (temp.right != null) {
                temp = temp.right;
            }
            
            //与根连起来
            temp.right = root;
            root.left = temp;
        }
        
        //对右子树进行变换
        TreeNode rightHead = Convert(root.right);
        if (rightHead != null) {
            //与根连起来
            root.right = rightHead;
            rightHead.left = root;
        }
        
        return leftHead == null ? root : leftHead;
    }
}

16.二叉搜索树的第k个节点AC

《剑指Offer》题目63

package tree;

//nowcoder pass
public class KthNode {

    private int index = 0;
    private TreeNode kthNode = null;
    
    public TreeNode KthNode(TreeNode pRoot, int k)
    {
        inOrder(pRoot, k);
        return this.kthNode;
    }
    
    public void inOrder(TreeNode pRoot, int k) {
        if (pRoot != null) {
            inOrder(pRoot.left, k);
            index++;
            if (index == k) {
                this.kthNode = pRoot;
                return;
            }
            inOrder(pRoot.right, k);
        }
    }
}

17.判断是否为二叉搜索树的后序遍历序列AC

《剑指Offer》题目24

package tree;
/**
二叉搜索树的后序遍历序列


题目描述
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。
如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。


思路:
后序遍历为左,右,根
由二叉搜索树的性质,左子树的值全部小于根的值,右子树的值全部大于根的值
 */
//nowcoder pass
public class Solution10 {
    public boolean VerifySquenceOfBST(int[] sequence) {
        if (sequence == null || sequence.length == 0) {
            return false;
        }
        return verify(sequence, 0, sequence.length - 1);
    }
    
    public boolean verify(int[] sequence, int start, int end) {
        if (start >= end) {
            return true;
        }
        
        int rootVal = sequence[end];
        //判断左子树
        int rightStart = end;
        for (int i = start; i < end; i++) {
            if (sequence[i] > rootVal) {
                rightStart = i;
                break;
            }
        }
        //判断右子树
        for (int i = rightStart; i < end; i++) {
            if (sequence[i] < rootVal) {
                return false;
            }
        }
        
        return verify(sequence, start, rightStart-1) && verify(sequence, rightStart, end-1);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值