二叉搜索树与简单递归98、99、108、530、701、450、669、501、538、剑指36

这篇博客详细介绍了与二叉搜索树相关的各种操作,包括验证有效性、恢复顺序、转换为平衡树、插入节点、删除节点、计算最小绝对差、修剪节点、查找众数以及转换为累加树。通过递归和中序遍历等算法,展示了如何高效地解决这些问题。
摘要由CSDN通过智能技术生成

98、验证二叉搜索树

给定一个二叉树,判断其是否是一个有效的二叉搜索树。

假设一个二叉搜索树具有如下特征:

  • 节点的左子树只包含小于当前节点的数。
  • 节点的右子树只包含大于当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

在这里插入图片描述

在这里插入图片描述
思路一:递归
不能单纯判断当前节点与左右的大小,比如

    5
 4     6
     3   7

这个3比4小,是不可以的。所以:

  • 对于左节点,当前节点及之后节点的最大值是他的父节点;
  • 对于右节点,当前节点及之后节点的最小值是他的父节点;
import math
class Solution:
    def isValidBST(self, root: TreeNode) -> bool:
        def dfs(root,maxval,minval):
            if not root:
                return True
            if root.val>=maxval or root.val<=minval:
                return False
            return dfs(root.left,root.val,minval) and dfs(root.right,maxval,root.val)
        
        return(dfs(root,math.inf,-math.inf))
class Solution {
    public boolean isValidBST(TreeNode root) {
        return dfs(root,Long.MIN_VALUE,Long.MAX_VALUE);
    }
    public boolean dfs(TreeNode root,long min,long max){
        if(root==null) return true;
        if(root.val>=max || root.val<=min) return false;
        return dfs(root.left,min,root.val) && dfs(root.right,root.val,max);
    }
}

思路二:中序遍历
若中序遍历的结果是严格递增的,则符合题意,用 pre 记录前一个数的大小
中序遍历

class Solution {
    TreeNode pre;
    boolean res;
    public boolean isValidBST(TreeNode root) {
        res=true;
        dfs(root);
        return res;
    }
    public void dfs(TreeNode root){
        if(root==null) return;
        dfs(root.left);
        if(pre!=null && pre.val>=root.val){
            res=false;
            return;
        }
        pre=root;
        dfs(root.right);
    }
}

99、恢复二叉搜索树

在这里插入图片描述
思路:中序遍历,用全局变量pre,t1,t2;pre记录前一个节点,t1,t2记录要交换的两个节点,即 t1>t2 的位置
注意:不能提前return,要寻找到最后一个 t2 的位置

class Solution {
    TreeNode pre,t1,t2;
    public void recoverTree(TreeNode root) {
        dfs(root);
        int temp=t1.val;
        t1.val=t2.val;
        t2.val=temp;
    }
    public void dfs(TreeNode root){
        if(root==null) return;
        dfs(root.left);
        if(pre!=null && pre.val>root.val){
            if(t1==null) t1=pre;
            t2=root;
            //returm
        }
        pre=root;
        dfs(root.right);
    }
}

108、将有序数组转换为二叉搜索树

在这里插入图片描述

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        if(nums.length==0) return null;
        return dfs(nums,0,nums.length-1);
    }
    public TreeNode dfs(int[] nums,int left,int right){
        if(left>right) return null;
        int mid=(left+right)/2;
        TreeNode root=new TreeNode(nums[mid]);
        root.left=dfs(nums,left,mid-1);
        root.right=dfs(nums,mid+1,right);
        return root;
    }
}

109、有序链表转换二叉搜索树

给定一个单链表的头节点 head ,其中的元素 按升序排序 ,将其转换为高度平衡的二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差不超过 1。

在这里插入图片描述

class Solution {
    public TreeNode sortedListToBST(ListNode head) {
        return dfs(head, null);
    }

    public TreeNode dfs(ListNode left, ListNode right) {
        if (left == right) {
            return null;
        }
        ListNode mid = getMedian(left, right);
        TreeNode root = new TreeNode(mid.val);
        root.left = dfs(left, mid);
        root.right = dfs(mid.next, right);
        return root;
    }

    public ListNode getMedian(ListNode left, ListNode right) {
        ListNode fast = left;
        ListNode slow = left;
        while (fast != right && fast.next != right) {
            fast = fast.next;
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }
}

530、二叉搜索树的最小绝对差

给你一棵所有节点为非负值的二叉搜索树,请你计算树中任意两节点的差的绝对值的最小值。
在这里插入图片描述
思路一:中序遍历存入列表,列表递增,相邻值之间差最小

class Solution {
    List<Integer>list=new ArrayList<>();
    public int getMinimumDifference(TreeNode root) {
        dfs(root);
        int res=Integer.MAX_VALUE;
        for(int i=1;i<list.size();i++){
            res=Math.min(res,list.get(i)-list.get(i-1));
        }
        return res;
    }
    public void dfs(TreeNode root){
        if(root==null) return;
        dfs(root.left);
        list.add(root.val);
        dfs(root.right);
    }
}

思路二:递归中序遍历
在递归中途更新最小值

class Solution {
    int res=Integer.MAX_VALUE;
    TreeNode pre;
    public int getMinimumDifference(TreeNode root) {
        dfs(root);
        return res;
    }
    public void dfs(TreeNode root){
        if(root==null) return;
        dfs(root.left);

        if(pre!=null) res=Math.min(res,root.val-pre.val);
        pre=root;

        dfs(root.right);
    }
}

701、二叉搜索树中的插入操作

给定二叉搜索树(BST)的根节点和要插入树中的值,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。

注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。
在这里插入图片描述
注意!!一定可以插在树的叶子上

class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
        //一定可以插在树的叶子上
        if(root==null){
            TreeNode node=new TreeNode(val);
            return node;
        }
        if(root.val>val) root.left=insertIntoBST(root.left,val);
        if(root.val<val) root.right=insertIntoBST(root.right,val);

        return root;
    }
}

450、删除二叉搜索树中的节点

在这里插入图片描述
在这里插入图片描述
思路:

public TreeNode deleteNode(TreeNode root, int key) {
        if(root==null) return null;
        if(root.val==key){
			单层逻辑
        }
        if(root.val>key) root.left=deleteNode(root.left,key);
        if(root.val<key) root.right=deleteNode(root.right,key);
        return root;
    }

递三步:
①终止条件
②单层逻辑
③处理左右

每一层有以下五种情况:

  • 第一种情况:没找到删除的节点,遍历到空节点直接返回了 找到删除的节点
  • 第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
  • 第三种情况:删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位,返回右孩子为根节点
  • 第四种情况:删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点
  • 第五种情况:左右孩子节点都不为空,则将删除节点的左孩子,放到删除节点的右子树的 最左面节点 的左孩子上,返回删除节点右孩子为新的根节点。

eg 5放在8下,9接替7的位置
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        if(root==null) return null;//一
        if(root.val==key){
            //二三四
            if(root.left==null) return root.right;
            else if(root.right==null) return root.left;
            //五,都不为空
            else{
                TreeNode node=root.right;//找到右子树最左边的节点
                while(node.left!=null) node=node.left;
                node.left=root.left;//root的左节点搬下来
                root=root.right;//替换root
                return root;
            }
        }
        if(root.val>key) root.left=deleteNode(root.left,key);
        if(root.val<key) root.right=deleteNode(root.right,key);
        return root;
    }
}

669、修剪二叉搜索树

在这里插入图片描述
在这里插入图片描述

class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {
        if(root==null) return null;
        if(root.val<low) {
            TreeNode rnode=trimBST(root.right,low,high);
            return rnode;
        }
        if(root.val>high){
            TreeNode lnode=trimBST(root.left,low,high);
            return lnode;
        }
        root.left=trimBST(root.left,low,high);
        root.right=trimBST(root.right,low,high);
        return root;
    }
}

501、二叉搜索树中的众数

在这里插入图片描述
思路:
正常来说,中序遍历得到非递减数组,然后求数组众数即可,但消耗额外空间;

已知遍历顺序肯定是非递减,如果有相同的数肯定连在一起,可以在遍历途中维护一个表示当前数字出现几次的count,当前出现最对的次数maxCount,和记录前一个节点pre

  • cur==pre; count++;
  • 否则,清空count
  • 如果count>maxCount,清空res,重新开始记录
class Solution {
    List<Integer>res=new ArrayList<>();
    TreeNode pre=null;
    int count=0;
    int maxCount=0;
    public int[] findMode(TreeNode root) {
        find(root);
        return res.stream().mapToInt(Integer::intValue).toArray();
    }
    public void find(TreeNode root){
        if(root==null) return;

        find(root.left);

        int cur=root.val;
        //计数
        if(pre==null || root.val!=pre.val) count=1;//重新开始计数
        else count++;
        //检查是否max
        if(count==maxCount){
            res.add(root.val);
        }else if(count>maxCount){
            res.clear();
            res.add(root.val);
            maxCount=count;
        }
        pre=root;

        find(root.right);
    }
}

538、把二叉搜索树转换为累加树

给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。
在这里插入图片描述
按【右中左】的顺序累加,就是一个有序数组[2, 5, 13],求从后到前的累加数组,也就是[20, 18, 13]

class Solution {
    int pre=0;
    public TreeNode convertBST(TreeNode root) {
        if(root==null) return null;
        convertBST(root.right);

        root.val+=pre;
        pre=root.val;

        convertBST(root.left);
        return root;
    }
}

剑指36、二叉搜索树与双向链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。

我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。

当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。

/*
// Definition for a Node.
class Node {
    public int val;
    public Node left;
    public Node right;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val,Node _left,Node _right) {
        val = _val;
        left = _left;
        right = _right;
    }
};
*/
class Solution {
    Node pre,head;
    public Node treeToDoublyList(Node root) {
        if(root==null) return null;

        dfs(root);
        head.left=pre;
        pre.right=head;
        return head;
    }
    public void dfs(Node root){
        if(root==null) return;
        dfs(root.left);

        if(pre==null) head=root;
        else pre.right=root;
        root.left=pre;
        pre=root;

        dfs(root.right);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值