LeetCode刷题—二叉搜索树的套路

本文针对BST 的基础操作:判断 BST 的合法性(98)、增(701)、删(450)、查(700)。以几道题来总结出套路模板,以一敌十!
98,验证二叉搜索树,medium
700,二叉搜索树中的搜索,easy
701,二叉搜索树中的插入操作,medium
450,删除二叉搜索树中的节点,medium

98,验证二叉搜索树,medium

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

示例 1:

输入:
    2
   / \
  1   3
输出: true

示例 2:

输入:
    5
   / \
  1   4
     / \
    3   6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
     根节点的值为 5 ,但是其右子节点值为 4 。
  • 方法一:递归。

    • 思路:很容易想到对于节点root,采用递归判断左子节点的值比它小,右子节点的值比它大,但注意:对每个节点都满足也不一定是BST树,如:

          5
         / \
        1   6
           / \
          4   7
      

      没有满足右子树的节点都比root值大。

      建立辅助函数,增加最小节点min和最大节点max 作为辅助函数的参量。对于root,比较root.val与当前的min.valmax.val,再对root.leftroot.right 进行递归操作。

    • 代码:

      class Solution {	
      	public boolean isValidBST(TreeNode root) {
              return healper(root, null, null);
          }
          //是BST树必须满足 min.val < root.val < max.val
          public boolean healper(TreeNode root, TreeNode min, TreeNode max){
              if(root == null) return true;
              if(min != null && min.val >= root.val) return false;
              if(max != null && max.val <= root.val) return false;
          //左子树范围的最小值是min.val,最大值是当前节点的值,也就是root的值,因为左子树的值要比当前节点小
          //右子数范围的最大值是max.val,最小值是当前节点的值,也就是root的值,因为右子树的值要比当前节点大
              return healper(root.left, min, root) && healper(root.right, root, max);
          }
      }
      
  • 方法二:中序遍历。

    • 思路:中序遍历的结果是从小到大排序的,建立一个全局遍历pre代表前一个节点,当前节点与前一个节点进行比较。

      • 递归:

        class Solution {
            //中序遍历,代表前一个节点
            TreeNode pre;
        //中序遍历,递归
            public boolean isValidBST(TreeNode root) {
                if(root == null) return true;
                // 访问左子树
                if(!isValidBST(root.left)) return false;
                // 访问当前节点,如果不是按照从小到大的顺序,则返回false;否则pre右移,继续比较
                if(pre != null && pre.val >= root.val) return false;
                pre = root;
                // 访问右子树
                if(!isValidBST(root.right)) return false;
                return true;
            }
        }
        
      • 迭代:

        class Solution {
            //中序遍历,代表前一个节点
            TreeNode pre;
            public boolean isValidBST(TreeNode root) {
                if(root == null) return true;
                Stack<TreeNode> stack = new Stack<>();
                while(!stack.isEmpty() || root != null){
                    while(root != null){
                        stack.push(root);
                        root = root.left;
                    }
                    root = stack.pop();
                    if(pre != null && pre.val >= root.val) return false;
                    pre = root;
                    root = root.right;
                }
                return true;
            }
        }
        
  • 易错点:采用递归方法只与左右子节点比较,但没有保证整棵树是BST树。

700,二叉搜索树中的搜索,easy

给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。

例如,

给定二叉搜索树:

    4
   / \
  2   7
 / \
1   3

和值: 2
你应该返回如下子树:

  2     
 / \   
1   3

在上述示例中,如果要找的值是 5,但因为没有节点值为 5,我们应该返回 NULL。

  • 思路:对于普通二叉树,判断是否存在值为 val 的节点

    public boolean search(TreeNode root, int val) {
        if(root == null) return false;
        if(root.val == val) return true;
        return search(root.left, val) || search(root.right, val);
    }
    

    对于BST树,利用其左小右大的规律,不需要递归地搜索两边,类似二分查找思想,根据valroot.val的大小比较,就能排除一边。

  • 代码:

    class Solution {
        public TreeNode searchBST(TreeNode root, int val) {
            if(root == null) return false;
        	if(root.val == val) return true;
            if(root.val < val) return searchBST(root.right, val);
             if(root.val > val) return searchBST(root.left, val);
        }
    }
    

    简化:

    public TreeNode searchBST(TreeNode root, int val) {
        if(root == null || root.val == val) return root;
        return val < root.val ? searchBST(root.left, val) : searchBST(root.right, val);
    }
    
  • 总结:BST树遍历搜索的框架为

    void BST(TreeNode root, int target) {
        if (root.val == target){
            // 找到目标节点,进行具体操作
        }
        //目标值target比当前节点值大,在右子树中找
        if (root.val < target) 
            BST(root.right, target);
        //目标值target比当前节点值小,在左子树中找
        if (root.val > target)
            BST(root.left, target);
    }
    
701,二叉搜索树中的插入操作,medium

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

注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。

示例 1:

img

输入:root = [4,2,7,1,3], val = 5
输出:[4,2,7,1,3,5]
解释:另一个满足题目要求可以通过的树是:

img

示例 2:

输入:root = [40,20,60,10,30,50,70], val = 25
输出:[40,20,60,10,30,50,70,null,null,25]

示例 3:

输入:root = [4,2,7,1,3,null,null,null,null,null,null], val = 5
输出:[4,2,7,1,3,5]
  • 思路:

    对于函数TreeNode insertIntoBST(TreeNode root, int val),表示插入值为 val 的节点后的BST树。由上面总结的查找的模板,再加入修改的操作即可。一旦涉及「改」,函数就要返回TreeNode类型,并且对递归调用的返回值进行接收。

    • 方法:递归
    • 代码:
    class Solution {
        //查找 + 修改
        public TreeNode insertIntoBST(TreeNode root, int val) {
            if(root == null) return new TreeNode(val);
            //val值比当前节点值大,将找个值插入右子树,且赋给root.right
            if(root.val < val) root.right = insertIntoBST(root.right, val);
            if(root.val > val) root.left = insertIntoBST(root.left, val);
            return root;
        }
    }
    
450,删除二叉搜索树中的节点,medium

给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

一般来说,删除节点可分为两个步骤:

首先找到需要删除的节点;
如果找到了,删除它。
说明: 要求算法时间复杂度为 O(h),h 为树的高度。

示例:

root = [5,3,6,2,4,null,7]
key = 3

    5
   / \
  3   6
 / \   \
2   4   7

给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。

一个正确的答案是 [5,4,6,2,null,null,7], 如下图所示。

    5
   / \
  4   6
 /     \
2       7

另一个正确答案是 [5,2,6,null,4,null,7]。

    5
   / \
  2   6
   \   \
    4   7
  • 思路:还是查找 + 修改 类型,按照上面的遍历框架,写出初步解答:

    TreeNode deleteNode(TreeNode root, int key) {
        if(root == null) return null;
        // 找到啦,进行删除
        if (root.val == key) {
           //一些删除操作
        } 
        // key在左子树,对左子树进行删除操作并重新赋给root.left
        else if (root.val > key) {
            root.left = deleteNode(root.left, key);
        } 
        // key在右子树,对右子树进行删除操作并重新赋给root.right
        else{
            root.right = deleteNode(root.right, key);
        }
        return root;
    }
    

    找到要删除的节点后,删除操作有下面情况:

在这里插入图片描述

  1. 左右子节点有为空的(如12),返回非空的子节点

    deleteNode 函数即返回删除值为key的节点后的树)

    如果左右子节点都为空,则返回null

    if(root.left == null) return root.right;
    if(root.right == null) return root.left;
    
  2. 左右子节点都不为空,就要找到左子树的最大值或者右子树的最小值替换此root,并在左子树(或右子树)删除此节点。

    root.val = getMaxLeft(root.left).val;
    root.left = deleteNode(root.left, root.val);
    
  • 代码:

    class Solution {
        public TreeNode deleteNode(TreeNode root, int key) {
            if(root == null) return null;
            //找到了要删除的节点
            if(root.val == key){
                //1.有子节点为空
                if(root.left == null) return root.right;
                if(root.right == null) return root.left;
                //2.子节点非空,则由左子树的最大值或者右子树的最小值替换此root,并在左子树(或右子树)删除此节点
                root.val = getMaxLeft(root.left).val;
                root.left = deleteNode(root.left, root.val);
            }
            //key在右子树,对右子树进行删除操作,并把新的子树重新赋给root.right
            else if(root.val < key){
                root.right = deleteNode(root.right, key);
            }
            else
                root.left = deleteNode(root.left, key);
            return root;
        }
        //从root.left出发,找到root的左子树的最大值(向右找)
        public TreeNode getMaxLeft(TreeNode node){
            if(node == null) return null;
            while(node.right != null){
                node = node.right;
            }
            return node;
        }
    }
    
总结:
  • 由BST树的搜索框架,可以实现增(插)删改查,这几项的操作都是搜+改,做题可以先把框架写出来,再分析每步干什么。

    void BST(TreeNode root, int target) {
        if (root.val == target){
            // 找到目标节点,进行具体操作
        }
        //目标值target比当前节点值大,在右子树中找
        if (root.val < target) 
            BST(root.right, target);
        //目标值target比当前节点值小,在左子树中找
        if (root.val > target)
            BST(root.left, target);
    }
    
  • 对于BST树最大的特点就是左小右大,中序遍历的结果由小到大。

  • 递归最重要的就是弄清函数的含义,如果有返回值代表什么。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值