牛客编程题--必刷101之二叉树篇

本文详细介绍了二叉树的各种遍历方法(前序、中序、后序、层次遍历)及其非递归实现,包括按之字形顺序打印二叉树。此外,还涵盖了二叉树的相关操作,如最大深度、路径和、转换为双向链表、对称性检查、合并、镜像、判断是否为二叉搜索树、完全二叉树、平衡二叉树、最近公共祖先等。文章旨在通过递归思维帮助读者理解二叉树算法。
摘要由CSDN通过智能技术生成

大家好,我是小曾哥,我又来了,继续给大家分享牛客TOP101之二叉树篇,在这篇中,应该会更浅显易懂,能够培养递归的思维方式,以递归的方式来解决问题,创作不易,可以先收藏再看。
两万字的干货,希望能够给大家有所帮助!
在这里插入图片描述
在这里插入图片描述

1、前、中、后序遍历

题目描述
给你二叉树的根节点 root ,返回它节点值的前序遍历。

输入:{1,#,2,3}
返回值:[1,2,3]

递归方案

前序:中、左、右
中序:左、中、右
后续:左、右、中

只需要修改dfs函数的顺序即可。

public int[] preorderTraversal (TreeNode root) {
        // write code here
        List<Integer> list = new ArrayList<>();
        dfs(list,root);
        int[] res = new int[list.size()];
        for(int i =0;i<list.size();i++){
            res[i] = list.get(i);
        }
        return res;
    }
    
    public void dfs(List<Integer> list, TreeNode root){
        if(root!=null){
            list.add(root.val);
            dfs(list,root.left);
            dfs(list,root.right);
        }
    }

非递归方案(栈)

	// 新建列表list
	private List<Integer> list = new ArrayList<>();
    public int[] preorderTraversal (TreeNode root) {
        // write code here
        if(root==null) return new int[0];
        // 新建栈 stack
        Stack<TreeNode> stack = new Stack<>();
        // 首先将头结点入栈
        stack.push(root);
        // 判断条件:栈是否为空
        while(!stack.isEmpty()){
        	// 取出栈的元素,将node加入列表中
            TreeNode node = stack.pop();
            list.add(node.val);
            // 栈特性:先进后出,因此先node.right ,后node。left
            if(node.right !=null) stack.push(node.right);
            if(node.left !=null) stack.push(node.left);   
        }
        // 创建数组
        int[] res = new int[list.size()];
        // 将列表中list的值保存到数组中。
        for(int i=0;i<list.size();i++){
            res[i] = list.get(i);
        }
        return res;
    }

2、层次遍历

题目描述:给定一个二叉树,返回该二叉树层序遍历的结果,(从左到右,一层一层地遍历)
例如:
给定的二叉树是{3,9,20,#,#,15,7},
在这里插入图片描述
该二叉树层序遍历的结果是
[[3],[9,20],[15,7]]

递归

具体步骤
1、定义递归函数功能:遍历当前层depth的所有结点并收集结果到list
2、depth用来表示层数,作为list的索引
3、每次递归时,初始化集合,该集合用来保存层数为depth时的结点值,depth相当于结果集合list的索引
4、递归直到叶子节点后,收集层数为depth的结果集,接着继续递归右子结点

public ArrayList<ArrayList<Integer>> levelOrder (TreeNode root) {
        // 递归
        ArrayList<ArrayList<Integer>> res = new ArrayList<>();
        if(root ==null) return res;
        dfs(res,root,0);
        return res;    
    }
    // 递归函数功能:遍历当前层depth的所有结点并收集结果到list
    // depth用来表示层数,作为list的索引
    public void dfs(ArrayList<ArrayList<Integer>> list, TreeNode root,int depth){
        if(root == null) return;
        // 初始化集合,该集合用来保存层数为depth时的结点值,depth相当于结果集合list的索引
        if(list.size() == depth){
            list.add(new ArrayList<>());
        }
        
        list.get(depth).add(root.val);
        dfs(list,root.left,depth+1);
        dfs(list,root.right,depth+1);
         
    }

非递归(队列)

public ArrayList<ArrayList<Integer>> levelOrder (TreeNode root) {
        // 非递归
        ArrayList<ArrayList<Integer>> res = new ArrayList<>();
        if(root ==null) return res;
        // 定义队列
        // 队列保存每一层所有结点
        Queue<TreeNode> queue = new LinkedList<>();
        // 先放入根节点
        queue.offer(root);
        while(!queue.isEmpty()) {
            // 收集当前层的所有结点的值
            ArrayList<Integer> list = new ArrayList<>();
            // ·当前层的节点数量
            int count = queue.size();
            // 遍历每一层
            while(count-- > 0) {
                // 从对头取出节点
                TreeNode node = queue.poll();
                // 收集结果
                list.add(node.val);
                // 左右节点按顺序加到队尾
                if(node.left != null) {
                    queue.offer(node.left);
                }
                if(node.right != null) {
                    queue.offer(node.right);
                }
            }
            res.add(list);
        }
        return res;
        
    }

3 按之字形顺序打印二叉树

使用队列 + 栈

import java.util.ArrayList;
import java.util.Stack;
 
public class Solution {
    public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
        if(pRoot == null)return res;
        Stack<Integer> stack = new Stack<Integer>();//stack仅用于偶数层翻转val
        ArrayList<TreeNode> queue = new ArrayList<TreeNode>();//queue是奇偶共用
        queue.add(pRoot);
        int layer = 1;//层数用layer (区别于深度depth = layer-1)
        while(!queue.isEmpty()){
            ArrayList<Integer> list = new ArrayList<Integer>();//新建行
            int size = queue.size();//出本层前记录size,不然难以做到层数的切分  //提前写出来,因为size会变
            for(int i=0; i<=size-1; ++i){
                TreeNode node = queue.get(0);//一定要新建node副本,不然是引用会变
                queue.remove(0);
                if(layer % 2 == 1){
                    list.add(node.val);
                }
                else{//偶数行,需要栈翻转
                    stack.push(node.val);
                }
                if(node.left != null)queue.add(node.left);
                if(node.right != null)queue.add(node.right);
            }
            while(!stack.isEmpty()){
                list.add(stack.pop());//偶数层一次性添加,奇数层一个个添加
            }
            res.add(list);
            ++layer;//本层结束,层数加一
        }
        return res;
    }
}//时间、空间复杂度,都是树的规模 O(N)

使用队列 + 头插法(ArrayList)

import java.util.ArrayList;
 
public class Solution {//方法2和方法1相比,代码区别仅仅是把stack相关删去,然后换成ArrayList头插法
    public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
        if(pRoot == null)return res;
        ArrayList<TreeNode> queue = new ArrayList<TreeNode>();
        queue.add(pRoot);
        int layer = 1;
        while(!queue.isEmpty()){
            ArrayList<Integer> list = new ArrayList<Integer>();
            int size = queue.size();
            for(int i=0; i<=size-1; ++i){
                TreeNode node = queue.get(0);
                queue.remove(0);
                if(layer % 2 == 1){
                    list.add(node.val);
                }
                else{
                    list.add(0,node.val);//头插法,逆序 //【代码简洁,但效率比Stack低】
                }
                if(node.left != null)queue.add(node.left);
                if(node.right != null)queue.add(node.right);
            }
            res.add(list);
            ++layer;
        }
        return res;
    }
}//时间、空间都是 O(N)

4、二叉树的最大深度

直接用递归方法

public int maxDepth (TreeNode root) {
        // write code here
        if(root ==null)
            return 0;
        int lDepth = maxDepth(root.left);
        int rDepth = maxDepth(root.right);
        return (lDepth > rDepth ? lDepth :rDepth)+1; 
    }

5、二叉树中和为某一值的路径(一)

题目描述:给定一个二叉树root和一个值 sum ,判断是否有从根节点到叶子节点的节点值之和等于 sum 的路径。
1.该题路径定义为从树的根结点开始往下一直到叶子结点所经过的结点
2.叶子节点是指没有子节点的节点
3.路径只能从父节点到子节点,不能从子节点到父节点
4.总节点数目为n
在这里插入图片描述
直接用递归遍历

public boolean hasPathSum (TreeNode root, int sum) {
        // write code here
        if(root == null){
            return false;
        }
        // 只有根节点,满足值要求
        if(root.left == null && root.right == null && root.val == sum){
            return true;
        }
        // 递归遍历
        return hasPathSum(root.left, sum-root.val) || hasPathSum(root.right,sum - root.val);
        
    }

6、二叉搜索树与双向链表

题目描述
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。如下图所示
在这里插入图片描述
输入描述:
二叉树的根节点
返回值描述:
双向链表的其中一个头节点。

输入:{10,6,14,4,8,12,16}
返回值:From left to right are:4,6,8,10,12,14,16;From right to left are:16,14,12,10,8,6,4;
说明:输入题面图中二叉树,输出的时候将双向链表的头节点返回即可。

中序遍历+数组

已知将二叉搜索树进行中序遍历可以得到由小到大的顺序排列,因此本题最直接的想法就是进行中序遍历。
将中序遍历的结果用数组存储下来,得到的数组是有从小到大顺序的。最后将数组中的结点依次连接即可
在这里插入图片描述

import java.util.*;
public class Solution {
    
    ArrayList<TreeNode> list = new ArrayList<>();
    // 中序遍历
    public void preorder(TreeNode root){
        if(root == null) return;
        preorder(root.left);
        list.add(root);
        preorder(root.right);
    }
    
    public TreeNode Convert(TreeNode pRootOfTree) {
        // 中序遍历 + 数组
        if(pRootOfTree == null) return pRootOfTree;
        preorder(pRootOfTree);
        // 根据列表中的关系进行结点连接
        for(int i =0;i<list.size()-1;i++){
            list.get(i).right = list.get(i+1);
            list.get(i+1).left = list.get(i);
        }
        return list.get(0);  
    }
}

中序遍历优化

依旧采取中序遍历。
根据题目的要求1,不能创建新的结点,而方法一的数组中存储的其实是结点,并不满足题意;所以需要在中序遍历的过程中,直接对结点的指针进行调整。
调整的思路如下:
(1)使用一个指针preNode指向当前结点root的前继。(例如root为指向10的时候,preNode指向8,如图所示:)
在这里插入图片描述
(2)对于当前结点root,有root->left要指向前继preNode(中序遍历时,对于当前结点root,其左孩子已经遍历完成了,此时root->left可以被修改。);同时,preNode->right要指向当前结点(当前结点是preNode的后继),此时对于preNode结点,它已经完全加入双向链表。

具体步骤:
1、特殊情况,二叉树为空,则直接返回 null
2、创建 保留上一个结点 pre,返回链表结点 root
3、递归遍历左子树;root = pRootOfTree
4、遍历当前结点,并修改为双向链表 pRootOfTree.left=pre; pre.right=pRootOfTree;
5、更新 pre = pRootOfTree
6、递归遍历右子树
7、递归结束返回 root

import java.util.*;
public class Solution {
    
    TreeNode pre = null;
    TreeNode root = null;
    public TreeNode Convert(TreeNode pRootOfTree) {
        if(pRootOfTree == null)
            return null;
        // 遍历左子树
        Convert(pRootOfTree.left);
        // 判断特殊情况
        if(root == null){
            root = pRootOfTree;
        }
        // 修改成双向链表
        if(pre!=null){
            pRootOfTree.left = pre;
            pre.right = pRootOfTree;
        }
        // 更新pre
        pre = pRootOfTree;
        // 继续递归右子树
        Convert(pRootOfTree.right);
        return root;
    }
}

7、对称的二叉树

题目描述:给定一棵二叉树,判断其是否是自身的镜像(即:是否对称)
例如:下面这棵二叉树是对称的
在这里插入图片描述
下面这棵二叉树不对称。
在这里插入图片描述

示例1
输入:{1,2,2,3,4,4,3}
返回值:true
输入:{8,6,9,5,7,7,5}
返回值:false

分析题目:
若满足对称二叉树,必须满足:

1. L->val == R->val
2. L->left->val == R->right->val
3. L->right->val == R->left->val

在这里插入图片描述
具体步骤:自顶向下,递归求解即可
1、设置一个递归函数isSame(r1, r2),表示如果对称,返回true,否则返回false
2、递归终止条件:r1null && r2null, 直接返回true,否则,如果只有一个为null,返回false
3、下一步递归:如果r1->val == r2->val, 则isSame(root1->left, root2->right) && isSame(root1->right, root2->left);

public class Solution {
    
    public boolean isSame(TreeNode root1, TreeNode root2){
        if(root1 ==null && root2 == null) return true;
        if((root1 == null && root2 !=null) || (root1 !=null && root2 ==null)) return false;
        return root1.val == root2.val && isSame(root1.left, root2.right) && isSame(root1.right, root2.left);
    }
    
    boolean isSymmetrical(TreeNode pRoot) {
        return isSame(pRoot, pRoot);
    }
}

8、合并二叉树

题目描述:已知两颗二叉树,将它们合并成一颗二叉树。合并规则是:都存在的结点,就将结点值加起来,否则空的位置就由另一个树的结点来代替。
两颗二叉树是:
tree1
在这里插入图片描述
tree2
在这里插入图片描述
合并后的树为
在这里插入图片描述

示例1
输入:{1,3,2,5},{2,1,3,#,4,#,7}
返回值:{3,4,5,5,4,#,7}

思路:递归
1、两个节点的值求和并赋给t1
2、递归合并t1、t2的左右节点。

public class Solution {
    
    public TreeNode mergeTrees (TreeNode t1, TreeNode t2) {
        // write code here
        if(t1 == null) return t2;
        if(t2 == null) return t1;
        t1.val += t2.val;
        t1.left = mergeTrees(t1.left, t2.left);
        t1.right = mergeTrees(t1.right, t2.right);
        return t1;
    }
}

9、二叉树的镜像

题目描述:操作给定的二叉树,将其变换为源二叉树的镜像。
源二叉树
在这里插入图片描述
镜像二叉树
在这里插入图片描述

示例1:
输入:{8,6,10,5,7,9,11}
返回值:{8,10,6,11,9,7,5}
说明:如题面所示

解题思路:递归
在这里插入图片描述
具体步骤:
1、特判:如果pRoot为空,返回空
2、交换左右子树
3、把pRoot的左子树放到Mirror中镜像一下
4、把pRoot的右子树放到Mirror中镜像一下
5、返回根节点root

递归方法一:
public TreeNode Mirror (TreeNode pRoot) {
        // 空树返回
        if(pRoot == null) return null;
        if(pRoot.left !=null || pRoot.right !=null){
            TreeNode temp = pRoot.left;
            pRoot.left = pRoot.right;
            pRoot.right = temp;
            
            Mirror(pRoot.left);
            Mirror(pRoot.right);
        }
        return pRoot;
    }


递归方法二:
public TreeNode Mirror (TreeNode pRoot) {
        // 空树返回
        if(pRoot == null) return null;
        //递归子树
        TreeNode left = Mirror(pRoot.left);
        TreeNode right = Mirror(pRoot.right);
        // 交换
        pRoot.left = right;
        pRoot.right = left;
        
        return pRoot;
    }

9、判断是不是二叉搜索树

题目描述:给定一个二叉树根节点,请你判断这棵树是不是二叉搜索树。

二叉搜索树满足每个节点的左子树上的所有节点均小于当前节点且右子树上的所有节点均大于当前节点。
在这里插入图片描述

输入:{1,2,3} 返回值:false
输入:{2,1,3} 返回值:true

递归方法

具体步骤:
提前设置最小值pre
1、先判断空
2、进行递归左子树,如果不满足,返回false
3、判断当前root值和pre值大小,如果root<pre,则返回false
4、更新pre值
5、然后递归右子树

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param root TreeNode类 
     * @return bool布尔型
     */
    int pre = Integer.MIN_VALUE; 
    
    public boolean isValidBST (TreeNode root) {
        // 首先判断为空
        if(root ==null) return true;
        if(!isValidBST(root.left))
            return false;
        if(root.val < pre) return false;
        // 更新最值
        pre = root.val;
        
        return isValidBST(root.right);
    }
}

10、判断是不是完全二叉树

题目描述:给定一个二叉树,确定他是否是一个完全二叉树。

完全二叉树的定义:若二叉树的深度为 h,除第 h 层外,其它各层的结点数都达到最大个数,第 h 层所有的叶子结点都连续集中在最左边,这就是完全二叉树。(第 h 层可能包含 [1~2h] 个节点)
在这里插入图片描述

输入:{1,2,3,4,5,6}
返回值:true
输入:{1,2,3,4,5,6,7}
返回值:true
输入:{1,2,3,4,5,#,6}
返回值:false

层次遍历

使用层次遍历,每一层从左到右遍历节点。

由于判断完全二叉树,当遍历当前层时如果遇到空节点,如果该空节点右侧还有节点,说明该树一定不是完全二叉树,直接返回false,遍历完返回true;
在这里插入图片描述

public boolean isCompleteTree (TreeNode root) {
        // 层次遍历方法
        // 标记空节点
        boolean target = true;
        if(root == null) return true;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        
        while(!queue.isEmpty()){
            TreeNode cur = queue.poll();
            //如果当前结点为空结点,将标记位left设置为false
            if(cur == null){
                target = false;
            }else{
               // 遇到空节点直接返回false
                if(target == false) return false;
                queue.offer(cur.left);
                queue.offer(cur.right);
                
                }
        }
        return true;
           
    }
}

深度优先遍历

具体方法
本题是需要判断完全二叉树,而完全二叉树有下面公式,如果某个节点索引是i,那么他的左右子节点为(索引从1开始)

left=2∗ileft = 2ileft=2∗i
right=2∗i+1right = 2
i+1 right=2∗i+1

两个计数操作
计数当前遍历访问的节点是第几个节点
计数在完全二叉树中,当前访问的节点在完全二叉树中的编号
如果最终得到的两个值相同,说明该树是一棵完全二叉树

如果最终得到的两个值不同,说明该树不是一棵完全二叉树

11、判断是不是平衡二叉树

描述
输入一棵节点数为 n 二叉树,判断该二叉树是否是平衡二叉树。
在这里,我们只需要考虑其平衡性,不需要考虑其是不是排序二叉树
平衡二叉树(Balanced Binary Tree),具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
样例解释:
在这里插入图片描述
样例二叉树如图,为一颗平衡二叉树
注:我们约定空树是平衡二叉树。

public class Solution {
    
    public int deep(TreeNode root){
        if(root == null) return 0;
        //递归左右子树
        int left = deep(root.left);
        int right = deep(root.right);
        //最大深度
        return left > right ? left+1 : right+1 ;
    }
    
    public boolean IsBalanced_Solution(TreeNode root) {
        //空树为平衡二叉树
        if(root == null) return true;
        int left = deep(root.left);
        int right = deep(root.right);
        // 左子树减去右子树值大于1
        if(left - right > 1 || right - left >1){
            return false;
        }
        return IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right);
    }
}

12、二叉搜索树的公共祖先

题目描述:给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。

1.对于该题的最近的公共祖先定义:对于有根树T的两个节点p、q,最近公共祖先LCA(T,p,q)表示一个节点x,满足x是p和q的祖先且x的深度尽可能大。在这里,一个节点也可以是它自己的祖先.
2.二叉搜索树是若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值; 若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值
3.所有节点的值都是唯一的。
4.p、q 为不同节点且均存在于给定的二叉搜索树中。

思路:非递归,利用二叉搜索树的特点。左子树<根节点<右子树

  • 若p,q都比当前结点的值小,说明最近公共祖先结点在当前结点的左子树上,继续检查左子树;
  • 若p,q都比当前结点的值大,说明最近公共祖先结点在当前结点的右子树上,继续检查右子树;
  • 若p,q中一个比当前结点的值大,另一个比当前结点的值小,则当前结点为最近公共祖先结点
public int lowestCommonAncestor (TreeNode root, int p, int q) {
        // //当前遍历结点
        TreeNode curnode = root;
        while(true){
            if(p< curnode.val && q < curnode.val) curnode = curnode.left;
            else if(p > curnode.val && q >curnode.val) curnode = curnode.right;
            else return curnode.val;
        }
    }

13、在二叉树中找到两个节点的最近公共祖先

题目描述:给定一棵二叉树(保证非空)以及这棵树上的两个节点对应的val值 o1 和 o2,请找到 o1 和 o2 的最近公共祖先节点。
在这里插入图片描述
直接使用递归算法:

import java.util.*;

/*
 * public class TreeNode {
 *   int val = 0;
 *   TreeNode left = null;
 *   TreeNode right = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param root TreeNode类 
     * @param o1 int整型 
     * @param o2 int整型 
     * @return int整型
     */
    public TreeNode helper(TreeNode root, int o1, int o2){
        if(root ==null || root.val ==o1 || root.val == o2)
            return root;
        TreeNode left = helper(root.left,o1,o2);
        TreeNode right = helper(root.right, o1,o2);
        //如果left为空,说明这两个节点在root结点的右子树上,我们只需要返回右子树查找的结果即可
        if(left == null) return right;
        if(right == null) return left;
        //如果left和right都不为空,说明这两个节点一个在root的左子树上一个在root的右子树上,
    //我们只需要返回cur结点即可。
        return root;
        
    }
    
    public int lowestCommonAncestor (TreeNode root, int o1, int o2) {
        // write code here
        return helper(root, o1,o2).val;
    }
}

14、序列化二叉树

实现两个函数,分别用来序列化和反序列化二叉树,不对序列化之后的字符串进行约束,但要求能够根据序列化之后的字符串重新构造出一棵与原二叉树相同的树。

二叉树的序列化(Serialize)是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树等遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#)

二叉树的反序列化(Deserialize)是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。
在这里插入图片描述
具体步骤:
1.采用层次遍历虽然易于人观察,但代码实现起来麻烦,本次借鉴leetcode官方题解,采用先序遍历实现序列化与反序列化。
在这里插入图片描述
2.先序遍历的按 root -> left subtree -> right subtree(根左右)的顺序递归进行,例如下面这幅图序列化结果
在这里插入图片描述
3.现在让我们反序列化"1!2!3!#!#!4!#!#!5!#!#!" ,同样采用先序遍历的思想,为了方便观察和后续操作,我们先将其分割成数组并存入链表。[1 , 2, 3, #, #, 4, #, #, 5, #, #]
先序遍历的数组总是可以分为三部分[ [根] , [左子树的先序序列] , [右子树的先序序列] ],且每部分的首位元素为该部分子树的根节点。
如 [ 1 ] 是 [2, 3, #, #, 4, #, #] [5, #, #] 的父节点。
而[ 2 ]又是[ 3, #, #] [4, #, #]的父节点
以此类推…

import java.util.*;
public class Solution {
    private String rSerialize(TreeNode root, String s){
        if(root ==null)  s+="#!";
        else{
            s+= root.val + "!";
            s = rSerialize(root.left, s);
            s = rSerialize(root.right, s);
        }
        return s;
    }
    String Serialize(TreeNode root) {
        // 序列化直接前序遍历即可
        return rSerialize(root,"");
    }
    
    private TreeNode rDeserialize(List<String> node_list){
        if(node_list.get(0).equals("#")|| node_list.get(0) == "#"){
            node_list.remove(0);
            return null;
        }
        //根据先序遍历特点,根据首位元素确定当前子树的根
        TreeNode root = new TreeNode(Integer.valueOf(node_list.get(0)));
        node_list.remove(0);
        root.left = rDeserialize(node_list);
        root.right = rDeserialize(node_list);
                                     
        return root;
    }
    
    TreeNode Deserialize(String str) {
       // 进行反序列化
        String[] nodes = str.split("!");
        List<String> node_list = new LinkedList<>(Arrays.asList(nodes));
        return rDeserialize(node_list);
         
    }
}

15、 重建二叉树

题目描述:给定节点数为 n 的二叉树的前序遍历和中序遍历结果,请重建出该二叉树并返回它的头结点。
例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出如下图所示。
在这里插入图片描述

递归方法

二叉树的前序遍历:根左右;中序遍历:左根右
由前序遍历知道根节点之后,能在中序遍历上划分出左子树和右子树。分别对中序遍历的左右子树递归进行这一过程即可建树。

在这里插入图片描述

public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] vin) {
        
        int n = pre.length;
        int m = vin.length;
        
        // 如果为空,则返回null
        if(n == 0 && m ==0) return null;
        // 构建根结点
        TreeNode root = new TreeNode(pre[0]);
        for(int i=0 ; i< m;  i++){
            // 找到前序遍历第一个数字在中序遍历的位置
            if(pre[0] == vin[i]){
                // 构建左子树
                root.left = reConstructBinaryTree(Arrays.copyOfRange(pre, 1, i+1), Arrays.copyOfRange(vin, 0, i ));
                // 构建右子树
                root.right = reConstructBinaryTree(Arrays.copyOfRange(pre, i+1, pre.length), Arrays.copyOfRange(vin, i+1, vin.length ));
                break;
            }
            
        }
        return root;
    }
}

16、输出二叉树的右视图

请根据二叉树的前序遍历,中序遍历恢复二叉树,并打印出二叉树的右视图
如输入[1,2,4,5,3],[4,2,5,1,3]时,通过前序遍历的结果[1,2,4,5,3]和中序遍历的结果[4,2,5,1,3]可重建出以下二叉树:
在这里插入图片描述
所以对应的输出为[1,3,5]。

思路1: 递归 + 层次遍历

1、先通过前序和中序遍历构建二叉树【可以直接用15题的递归代码】
2、然后再通过层次遍历,获得每层的最后一个结点,保存到列表中
3、再将list元素转到对应数组中。

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 求二叉树的右视图
     * @param xianxu int整型一维数组 先序遍历
     * @param zhongxu int整型一维数组 中序遍历
     * @return int整型一维数组
     */
    List<TreeNode> list = new ArrayList<>();
    
    public TreeNode reConstructBinaryTree(int [] pre,int [] vin) {
        
        int n = pre.length;
        int m = vin.length;
        
        // 如果为空,则返回null
        if(n == 0 && m ==0) return null;
        // 构建根结点
        TreeNode root = new TreeNode(pre[0]);
        for(int i=0 ; i< m;  i++){
            // 找到前序遍历第一个数字在中序遍历的位置
            if(pre[0] == vin[i]){
                // 构建左子树
                root.left = reConstructBinaryTree(Arrays.copyOfRange(pre, 1, i+1), Arrays.copyOfRange(vin, 0, i ));
                // 构建右子树
                root.right = reConstructBinaryTree(Arrays.copyOfRange(pre, i+1, pre.length), Arrays.copyOfRange(vin, i+1, vin.length ));
                break;
            }
            
        }
        return root;
    }
    // 层次遍历
    public void dfs(TreeNode root){
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            int size = queue.size();
            for(int i =0 ; i<size;i++){
                TreeNode p = queue.poll();
                // 将最后一个元素添加到list中
                if(i== size-1){
                    list.add(p);
                }
                if(p.left !=null) queue.offer(p.left);
                if(p.right !=null) queue.offer(p.right); 
            }
        }
    }
    
    
    public int[] solve (int[] xianxu, int[] zhongxu) {
        // 获取构建的二叉树
        TreeNode root = reConstructBinaryTree(xianxu, zhongxu);
        // 进行层次遍历
        dfs(root);
        int [] ans = new int[list.size()];
        for(int i = 0 ; i< list.size();i++){
            ans[i] = list.get(i).val;
        }
        return ans;
               
    }
}
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

研行笔录

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值