二叉搜索树的一些题目

161 阅读3分钟

二叉搜索树概念

二叉搜索树 : 满足 BST 性质
任意节点的关键码 >= 左子树所有节点的关键码
任意节点的关键码 <= 右子树所有节点的关键码

中序遍历【必然有序】。二叉搜索树操作的时间复杂度 为 O(h)-->   h 为 树高最坏一条链,即二叉搜索树在极端情况下会退化为一个链表

BST 检索问题

  • 从根递归查找
  • 根值 大于 val ,递归检索左子树
  • 根值 小于 val ,递归检索右子树。
  • 不存在 检索为NULL 的情况。

代码[代码随想录]

 // 递归,利用二叉搜索树特点,优化
    public TreeNode searchBST(TreeNode root, int val) {
        if (root == null || root.val == val) {
            return root;
        }
        if (val < root.val) {
            return searchBST(root.left, val);
        } else {
            return searchBST(root.right, val);
        }
    }

二叉搜索树插入数据的问题(其实就是检索问题 找对应位置插入)

  • 插入相同元素,节点应包含相同节点数量。
  • 插入不同元素,在对应位置插入关键码为 val 的结点。

二叉搜索树求前驱问题

前驱 : BST 中 小于 val 的最大节点

给定 val , 两种情况

  • val存在 : 检索右子树一直走到头
  • val 不存在,前驱在所有检索过程中经过的节点中,可以用变量存储一下祖先 image.png

二叉搜索树求后继问题

后继 : BST 中 大于 val 的最小节点
求法与 前驱类似 image.png

BST 删除问题

基于检索和求后继节点(前驱)实现。
检索 val : 1、 若 val 只有一颗子树**【直接删除】**,子树与父亲连接即可。若两颗子树,找到后继,先删除后继,再用后继节点代替val的位置

image.png

删除根节点示例 image.png

实战

二叉搜索树的插入操作

class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
    //  输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。
    if (root == null) return new TreeNode(val);
        if (val < root.val) {
          root.left = insertIntoBST(root.left,val);
        }
        else root.right = insertIntoBST(root.right,val);
        return root;
    }
}

后继者

class Solution {
    int ans = Integer.MAX_VALUE;
    public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
          // 找二叉搜索树的后继节点 基于检索
          if (root == null) return null;
          // 检索到对应节点
          if (root.val == p.val)  {
            //   后继 : 比 p.val 大的节点的最小值
            TreeNode next = p.right;
            if (next == null ) {
                if (ans == Integer.MAX_VALUE) return null;
                return new TreeNode(ans);
            }
            else {
                // next.left != null next 永不为  null
                while (next.left != null) next = next.left;
                return next;
            }
          }
        // 递归检索 根大
          if (root.val < p.val)  { return inorderSuccessor(root.right,p);}
          else {ans = Math.min(ans,root.val);return inorderSuccessor(root.left,p);}
    }
}
  • 总结 : 花费时间有点长。 检索到 val 的位置 , 具体卡壳 : 关键记录祖先代码 ans = Math.min(ans,root.val);由于 二叉搜索树的性质、【左小】【右大】 在 else 检索代码中,Math.min 一路记录了 【比他大的最小祖先】 即为后继 、当且仅当当此节点没有右节点时

二叉搜索树的删除操作

  • 思路 :二叉树的删除操作 可以基于 检索 + 找后继实现 具体看上面的图。
  • 两种情况分别是:这种重复的事情 我们可以交给递归 来完成。
  • 被删节点只有一棵子树,该节点直接替换被删除节点。完成调整
  • 被删节点名下有两棵子树,找【被删节点后继】 替换 【被删节点】 完成删除。

代码

class Solution {

    public TreeNode deleteNode(TreeNode root, int key) {    
        // 递归到死 都没有 与之匹配的
        if (root == null) return null;
        if (root.val == key) {
            // 找到了删除节点 是否 没有子树? case 1
            if (root.left == null && root.right == null) return null; 
            if (root.left == null) return root.right;
            if (root.right == null) return root.left;
            // 后继节点 找最大的最小的 我们发现 走到这里 next 用不为空 hh 少了一步 记录祖先的操作
            TreeNode next = root.right;
            while (next.left != null) next = next.left;
            // 删除 后继节点~ 
            root.right = deleteNode(root.right,next.val);
            // 替换 root 节点的值
            root.val = next.val;
            return root;

        }


        // 检索 二叉搜索树的代码 + 找后继 删除要接住 
        if (key < root.val) root.left = deleteNode(root.left , key);
        else  root.right = deleteNode(root.right,key);
        return root;
    }
}