JAVA-高频面试题汇总:二叉树(下)

前言

为了让小伙伴们更好地刷题,我将所有leetcode常考题按照知识点进行了归纳.

目录:

JAVA-高频面试题汇总:动态规划
JAVA-高频面试题汇总:字符串
JAVA-高频面试题汇总:二叉树(上)
JAVA-高频面试题汇总:二叉树(下)
JAVA-高频面试题汇总:回溯

JAVA-高频面试题汇总:二叉树(下):

接下来我对树这一知识点进行归纳总结,树的内容总结了二十多道,将分为2篇文章讲解,归纳基于JAVA语言,是我一边复习一边整理的,如有疑惑欢迎交流!
小编微信: Apollo___quan

11.二叉树的下一个节点

在这里插入图片描述

在这里插入图片描述

思路

仔细观察,可以把中序下一结点归为四种类型:

  1. 有右子树,下一结点是右子树中的最左结点,例如 B,下一结点是 H

  2. 无右子树,且结点是该结点父结点的左子树,则下一结点是该结点的父结点,例如 H,下一结点是 E

  3. 无右子树,且结点是该结点父结点的右子树,则我们一直沿着父结点追朔,直到找到某个结点是其父结点的左子树,如果存在这样的结点,那么这个结点的父结点就是我们要找的下一结点。例如 I,下一结点是 A。

  4. 无右子树,且结点是该结点父结点的右子树,则我们一直沿着父结点追朔,然而找不到某个结点是其父结点的左子树,例如 G,并没有符合情况的结点,所以 G 没有下一结点

    其中3、4代码可以合并成一种 (pNode.next!=null && pNode.next.left==pNode)

    /*
    public class TreeLinkNode {
        int val;
        TreeLinkNode left = null;
        TreeLinkNode right = null;
        TreeLinkNode next = null;
    
        TreeLinkNode(int val) {
            this.val = val;
        }
    }
    */
    public class Solution {
        public TreeLinkNode GetNext(TreeLinkNode pNode)
        {
            if(pNode==null) return null;
            if(pNode.right != null){  //第一种情况
                TreeLinkNode cur = pNode.right; 
                while(cur.left != null) {
                cur = cur.left;
                }
                return cur;
            }
            else{
                if(pNode.next!=null && pNode.next.left==pNode){ //第二种情况,注意pNode.next!=null
                    return pNode.next;
                    }
                else{
                    TreeLinkNode node2=pNode;
                    while(node2.next!=null&&node2!=node2.next.left) //包含了第三第四种情况
                    {  
                        node2=node2.next;
                    }
                    return node2.next;
                    }
                }
        }
    }
    
12.对称二叉树

在这里插入图片描述

思路

重点是要讨论left.left,right.right 以及left.right,right.left 所以最好把左右子树拿来做递归函数的参数

boolean isSymmetrical(TreeNode pRoot)
    {
        if(pRoot == null) return true; //空树为true
        return duicheng(pRoot.left, pRoot.right); //从左右子树单独递归,这个思路要牢记
    }
    public boolean duicheng(TreeNode left, TreeNode right){
        if(left==null&&right==null) return true; //判空要放前面,否则后面会空指针
        if(left==null||right==null||left.val!=right.val) return false;//判空要放前面,否则后面会空指针
        return duicheng(left.left,right.right)&&duicheng(left.right,right.left);
    }
13.对称二叉树

在这里插入图片描述

思路

重点是要讨论left.left,right.right 以及left.right,right.left 所以最好把左右子树拿来做递归函数的参数

boolean isSymmetrical(TreeNode pRoot)
    {
        if(pRoot == null) return true; //空树为true
        return duicheng(pRoot.left, pRoot.right); //从左右子树单独递归,这个思路要牢记
    }
    public boolean duicheng(TreeNode left, TreeNode right){
        if(left==null&&right==null) return true; //判空要放前面,否则后面会空指针
        if(left==null||right==null||left.val!=right.val) return false;//判空要放前面,否则后面会空指针
        return duicheng(left.left,right.right)&&duicheng(left.right,right.left);
    }
14.之字形打印二叉树

在这里插入图片描述

思路

1.考虑每一层打印顺序的问题,层序遍历是基础

2.把题中顺序都当作出栈顺序。创建两个栈,控制奇数偶数时左子树右子树的入栈顺序,来实现题中出栈顺序

public class Solution {
    Stack<TreeNode> stack1=new Stack<>();
    Stack<TreeNode> stack2=new Stack<>();  //创建两个栈
    ArrayList<Integer> res=new ArrayList<>();
    ArrayList<ArrayList<Integer> > list=new ArrayList<>();
    public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
       if(pRoot==null) return list;
        stack1.add(pRoot);
        while(!stack1.isEmpty()||!stack2.isEmpty()){ //两栈不都为空时说明没遍历完
            while(!stack1.isEmpty()){ 
                TreeNode node = stack1.pop();
                if(node.left!=null) stack2.add(node.left);  //根据规律,奇数时左子树先进栈
                if(node.right!=null) stack2.add(node.right);
                res.add(node.val);
            }
            list.add(res);
            res=new ArrayList<>();
            while(!stack2.isEmpty()){
                TreeNode node = stack2.pop();
                if(node.right!=null) stack1.add(node.right); //根据规律,偶数时右子树先进栈
                if(node.left!=null) stack1.add(node.left);
                res.add(node.val);
            }
           
            if(!res.isEmpty()) list.add(res); //避免最后一次遍历可能stack2为空跳过了,此时res是空列表被加入
            res=new ArrayList<>();
        }
        return list;
    }
}
15.二叉搜索树的第k大节点

在这里插入图片描述

思路

1.注意返回的是树还是节点

2.考虑提前返回的解法

class Solution { 
        int num = 0, res = 0; //num计数,res记录数值
    public int kthLargest(TreeNode root, int k) {
        recur(root, k);
        return res;
    }
    public void recur(TreeNode node, int k){
        if(node == null) return;
        recur(node.right, k); //因为是降序,从右子树开始遍历
        if(num == k) return; //提前返回,若num==k,则接下来的判断都是return;,一层层返回退出递归
        if(++num == k) { 
            res = node.val;
        }
        recur(node.left, k);
    }
}
16.最大二叉树

在这里插入图片描述

思路

简单解法,遍历数组找到最大值,最大值左边的为左子树,右边的是右子树。这种解法时间复杂度O(n^2)

如果有更好解法的欢迎评论哈,后续有想到我也会补充

class Solution {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
        if(nums.length ==0) return null;
        int max = nums[0], index = 0;
        for(int i = 0; i < nums.length; i++){ //遍历数组找到最大值并记录位置
            if(nums[i] >max){
                max = nums[i];
                index = i;
            }
        }
        TreeNode Left =  constructMaximumBinaryTree(Arrays.copyOfRange(nums, 0, index)); //左子树
        TreeNode Right =  constructMaximumBinaryTree(Arrays.copyOfRange(nums, index + 1, nums.length)); //右子树
        TreeNode node = new TreeNode(max);
        node.left = Left;
        node.right = Right;
        return node; //注意返回当前节点
    }
}
17.不同的二叉搜索树 II

这题乍一看和不同的二叉搜索树类似,注意区分,前者是用动态规划解的

在这里插入图片描述

思路

递归,附上官方解法
image-20210108110604709

class Solution {
    public List<TreeNode> generateTrees(int n) {
        if (n == 0) {
            return new LinkedList<TreeNode>();
        }
        return generateTrees(1, n);
    }

    public List<TreeNode> generateTrees(int start, int end) {
        List<TreeNode> allTrees = new LinkedList<TreeNode>();
        if (start > end) {
            allTrees.add(null);
            return allTrees;
        }

        // 枚举可行根节点
        for (int i = start; i <= end; i++) {
            // 获得所有可行的左子树集合
            List<TreeNode> leftTrees = generateTrees(start, i - 1);

            // 获得所有可行的右子树集合
            List<TreeNode> rightTrees = generateTrees(i + 1, end);

            // 从左子树集合中选出一棵左子树,从右子树集合中选出一棵右子树,拼接到根节点上
            for (TreeNode left : leftTrees) {
                for (TreeNode right : rightTrees) {
                    TreeNode currTree = new TreeNode(i);
                    currTree.left = left;
                    currTree.right = right;
                    allTrees.add(currTree);
                }
            }
        }
        return allTrees;
    }
}

18.将有序数组转换为二叉搜索树

在这里插入图片描述

思路

1.因为本题要求高度平衡,所以选择升序序列的中间元素作为根节点

2.如果数组是奇数长度,结果唯一,如果是偶数,结果不唯一(可以选择中间左边或中间右边节点作为根)。

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        return dfs(nums, 0, nums.length - 1);
    }

    private TreeNode dfs(int[] nums, int lo, int hi) {
        if (lo > hi) {
            return null;
        } 
        // 以升序数组的中间元素作为根节点 root。
        int mid = lo + (hi - lo) / 2;
        TreeNode root = new TreeNode(nums[mid]);
        // 递归的构建 root 的左子树与右子树。
        root.left = dfs(nums, lo, mid - 1);
        root.right = dfs(nums, mid + 1, hi); 
        return root;
    }
}
19.有序链表转换二叉搜索树

在这里插入图片描述

思路

1.与上一题对比,将通过数组索引定位中心元素改成快慢指针法找中心元素即可

2.快指针是慢指针的两倍速率,所以快指针达到尾结点时,慢指针到达中间节点

class Solution {
    ListNode globalHead;

    public TreeNode sortedListToBST(ListNode head) {
        globalHead = head;
        int length = getLength(head);
        return buildTree(0, length - 1);
    }

    public int getLength(ListNode head) {
        int ret = 0;
        while (head != null) {
            ++ret;
            head = head.next;
        }
        return ret;
    }

    public TreeNode buildTree(int left, int right) {
        if (left > right) {
            return null;
        }
        int mid = (left + right + 1) / 2;
        TreeNode root = new TreeNode();
        root.left = buildTree(left, mid - 1);
        root.val = globalHead.val;
        globalHead = globalHead.next;
        root.right = buildTree(mid + 1, right);
        return root;
    }
}
20.将二叉搜索树变平衡

image-20210108153340441.pn

思路

通用的方法,先将一棵树中序遍历出,然后按照前两题的方法,把中位数当根节点,递归构造二叉树

class Solution {
    public TreeNode balanceBST(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        zhongxu(list, root); // 中序遍历构造有序链表
        return BuildTree(list, 0, list.size() - 1);  // 有序链表构造平衡二叉树
    }
    public void zhongxu(List<Integer> list, TreeNode node){   //有序链表构造平衡二叉树
        if(node == null) return;
        zhongxu(list, node.left);
        list.add(node.val);
        zhongxu(list, node.right);
    }
    public TreeNode BuildTree(List<Integer> list, int start, int end){
        if(start > end) return null;
        int mid = start + (end - start) / 2;
        TreeNode root = new TreeNode(list.get(mid)); // 中间节点为root
        root.left = BuildTree(list, start, mid-1); // 递归构造左右子树
        root.right = BuildTree(list, mid+1, end);
        return root; // 返回root
    }
}

思路

通用的方法,先将一棵树中序遍历出,然后按照前两题的方法,把中位数当根节点,递归构造二叉树

class Solution {
    public TreeNode balanceBST(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        zhongxu(list, root); // 中序遍历构造有序链表
        return BuildTree(list, 0, list.size() - 1);  // 有序链表构造平衡二叉树
    }
    public void zhongxu(List<Integer> list, TreeNode node){   //有序链表构造平衡二叉树
        if(node == null) return;
        zhongxu(list, node.left);
        list.add(node.val);
        zhongxu(list, node.right);
    }
    public TreeNode BuildTree(List<Integer> list, int start, int end){
        if(start > end) return null;
        int mid = start + (end - start) / 2;
        TreeNode root = new TreeNode(list.get(mid)); // 中间节点为root
        root.left = BuildTree(list, start, mid-1); // 递归构造左右子树
        root.right = BuildTree(list, mid+1, end);
        return root; // 返回root
    }
}

总结

至此,二叉树常考题归纳完毕,后续会有其他类型题的更新!
JAVA-高频面试题汇总:动态规划
JAVA-高频面试题汇总:字符串
JAVA-高频面试题汇总:二叉树(上)
JAVA-高频面试题汇总:二叉树(下)

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 算法9-9~9-12是关于平衡二叉树的基本操作,包括插入、删除、旋转等操作。平衡二叉树是一种特殊的二叉搜索树,它的左右子树高度差不超过1,可以保证树的高度始终在log(n)级别,从而保证了树的查找、插入、删除等操作的时间复杂度都是O(log(n))。 具体来说,算法9-9是平衡二叉树的插入操作,它首先按照二叉搜索树的规则找到要插入的位置,然后通过旋转操作来保持平衡。算法9-10是平衡二叉树的删除操作,它也是通过旋转操作来保持平衡。算法9-11和9-12是平衡二叉树的旋转操作,包括左旋、右旋、左右旋和右左旋,这些操作可以使树重新达到平衡状态。 总之,平衡二叉树的基本操作是非常重要的,它们可以保证树的高度始终在log(n)级别,从而保证了树的查找、插入、删除等操作的时间复杂度都是O(log(n)),是一种非常高效的数据结构。 ### 回答2: 平衡二叉树是一种基于二叉查找树的数据结构,其在插入和删除节点时会自动调整,以保持树的平衡性。平衡二叉树的常见有AVL树、红黑树等。本文主要介绍平衡二叉树的基本操作,包括插入、删除和查找。 9-9 插入操作 在平衡二叉树中插入一个节点的过程和在二叉查找树中插入节点的过程类似。不同的是,在插入结束后,需要检查当前节点是否失去平衡并做出相应的调整。 在插入节点时,需要记录节点的高度(从叶节点到根节点的距离)。如果当前节点为空,则将新节点插入到该节点处并将高度设置为1;否则,比较新节点的值和当前节点的值,如果新节点的值小于当前节点的值,则将新节点插入到当前节点的左子树中并更新该节点的高度;如果新节点的值大于当前节点的值,则将新节点插入到当前节点的右子树中并更新该节点的高度。 插入结束后,需要检查当前节点是否失去平衡。我们可以用该节点的左子树高度和右子树高度之差来衡量它是否平衡。如果该节点的平衡因子大于1,则需要进行旋转操作,以恢复平衡。 9-10 删除操作 在平衡二叉树中删除一个节点需要分为以下三种情况: 1. 被删除的节点为叶子节点,直接删除即可。 2. 被删除的节点有一个子节点,将该子节点代替被删除的节点即可。 3. 被删除的节点有两个子节点,需要找到它的中序遍历下一个节点(即比它大的最小节点)代替被删除的节点。如果该节点有右子树,则中序遍历下一个节点为右子树中最小的节点;如果该节点没有右子树,则中序遍历下一个节点为它的某个祖先节点。 删除结束后,需要检查当前节点是否失去平衡。如果失去平衡,则需要进行旋转操作,以恢复平衡。 9-11 查找操作 在平衡二叉树中查找一个节点的过程和在二叉查找树中查找节点的过程类似。需要从根节点开始,比较查找的值和当前节点的值。如果查找的值小于当前节点的值,则在左子树中递归查找;如果查找的值大于当前节点的值,则在右子树中递归查找;如果查找的值等于当前节点的值,则返回该节点。 9-12 平衡因子 平衡二叉树的平衡因子定义为当前节点的左子树高度和右子树高度之差。如果平衡因子的绝对值大于1,则说明该节点失去了平衡。在平衡二叉树中,每个节点的平衡因子只能为-1、0或1。如果不是这三个值,则需要进行旋转操作以恢复平衡。 ### 回答3: 平衡二叉树是一种特殊的二叉搜索树,它的左右子树高度差不超过1。由于平衡二叉树对于插入、删除、查找等操作的时间复杂度都是O(logn),因此在许多应用场景中得到了广泛的应用。平衡二叉树的基本操作包括插入、删除、查找等,以下分别介绍: 9-9 插入操作:平衡二叉树的插入操作与普通二叉搜索树相同,只是插入后需要进行平衡处理,避免出现左右子树高度差不平衡的情况。例如插入节点x,需要先查找其应当插入的位置,然后通过旋转操作将其父节点与祖父节点一起旋转,使得树重新平衡。插入操作的时间复杂度为O(logn)。 9-10 删除操作:删除操作也类似于普通二叉搜索树,需要删除节点x后通过旋转操作迭代处理其祖先节点的平衡性,保证整个树的平衡性。删除操作的时间复杂度为O(logn)。 9-11 查找操作:查找操作与普通二叉搜索树相同,只是由于平衡二叉树的高度比较平衡,因此可以保证其查找效率较高。查找操作的时间复杂度为O(logn)。 9-12 平衡操作:平衡二叉树的平衡操作主要包括旋转操作和重构操作。旋转操作通过将子树旋转到左右子树高度相等来实现平衡,分为左旋和右旋两种。重构操作通过重新构建平衡二叉树的结构来保证整个树的平衡性。平衡操作的时间复杂度为O(1)。 综上所述,平衡二叉树是一种高效的数据结构,其插入、删除、查找等基本操作时间复杂度都为O(logn),通过平衡操作可以保证整个树的平衡性。在实际应用中,平衡二叉树被广泛应用于数据库、搜索引擎、红黑树等场景中,具有重要的实用价值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值