数据结构与算法篇之二叉搜索树

421 阅读2分钟

二叉搜索树,是二叉树中最常用的一种类型。二叉搜索树要求,在树中的任意一个节点,其左子树中的每个节点的值,都要小于这个节点的值,而右子树节点的值都大于这个节点的值。

二叉搜索树是为了实现快速查找而生的。不过,它不仅仅支持快速查找一个数据,还支持快速插入、删除一个数据。

题型一、二叉搜索树的递归

写递归的思路:

  1. 思考能否将问题分解为几个子问题来求解,如果可以那么递归解决。

  2. 明确递归函数的定义(返回值是什么、接收参数)。

二叉搜索树的查找

二叉搜索树的查找、插入操作只需将对象节点与根节点比较,根据比较的结果决定直接返回还是递归的查找左子树或右子树。

700. 二叉搜索树中的查找

image-20220523162224221.png

  1. 分解问题: search 根树 = return 根树 || search 左子树 || search 右子树

  2. 递归函数返回查找到的子树

var searchBST = function(root, val) {
    if (!root) return null;
    if (root.val === val) {
        return root;
    } else if (root.val > val) {
        return searchBST(root.left, val);
    } else {
        return searchBST(root.right, val);
    } 
};

二叉搜索树的插入

701. 二叉搜索树中的插入

image-20220523191925667.png

  1. 分解问题:插入到左子树 || 插入到右子树

  2. 递归函数返回插入操作完成后的根节点

var insertIntoBST = function(root, val) {
    if (!root) return new TreeNode(val);
    if (root.val > val) {
        root.left = insertIntoBST(root.left, val);
    }
    if (root.val < val) {
        root.right = insertIntoBST(root.right, val);
    }
    return root;
};

二叉搜索树的删除

二叉搜索树的删除操作比较复杂,针对要删除节点的子节点个数的不同,需要分三种情况来处理:

  1. 如果要删除的节点只有一个子节点是末端节点,两个子节点都为空,那么它可以直接删掉
  2. 如果要删除的节点只有一个子节点,那么只要让它非空子孩子接替自己的位置即可
  3. 如果要删除的节点有两个子节点,需要找到这个节点的右子树中最小节点,把它替换到要删除的节点上。然后再删除这个最小节点即可(因为是最小节点,所以肯定没有子节点了)

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

image-20220523192653074.png

var deleteNode = function(root, key) {
    // 递归函数定义:删除某节点,并返回删除后的 BST
    if (!root) return null;
    const getMin = root => {
        while (root.left) {
            root = root.left;
        }
        return root;
    }
    if (root.val === key) {
        if (!root.left) {
            return root.right;
        } else if (!root.right) {
            return root.left;
        } else {
            let minNode = getMin(root.right);
            root.val = minNode.val;
            root.right = deleteNode(root.right, minNode.val);
        }
    } else if (root.val < key) {
        root.right = deleteNode(root.right, key);
    } else {
        root.left = deleteNode(root.left, key);
    }
    return root;
};

98. 验证二叉搜索树

image-20220523163326998.png

  1. 分解问题:validate 根树= validate root.val || validate 左子树 || validate 右子树

  2. 递归函数返回表示是否是二叉搜索树的 boolean 值

var isValidBST = function(root) {
    const validate = (root, min = -Infinity, max = +Infinity) => {
        if (!root) return true;
        if (root.val >= max || root.val <= min) return false;
        return validate(root.left, min, root.val) && validate(root.right, root.val, max);
    }
    return validate(root);
};

235. 二叉搜索树的最近公共祖先

image.png

  1. 分解问题:

    • 最近公共祖先是根节点(p > root.val && q < root.val)
    • 最近公共祖先在左子树中(p < root.val && q < root.val)
    • 最近公共祖先在右子树中(p > root.val && q > root.val)
  1. 递归函数返回公共祖先节点
var lowestCommonAncestor = function(root, p, q) {
    if (p.val > root.val && q.val > root.val) {
        return lowestCommonAncestor(root.right, p, q);
    } else if (p.val < root.val && q.val < root.val) {
        return lowestCommonAncestor(root.left, p, q);
    } else {
        return root;
    }
};

669. 修剪二叉搜索树

image.png

  1. 分解问题

    • root.val < low,只需修剪右边即可
    • root.val > high,只需修剪左边即可
    • low < root.val < high,那么左右子树都需要
  2. 递归函数返回修剪好的二叉树

var trimBST = function(root, low, high) {
    //递归函数的定义:删除 BST 中小于 low 和大于 high 的所有节点,返回结果 BST
    if (!root) return null;
    if (root.val < low) {
        return trimBST(root.right, low, high);
    }
    else if (root.val > high) {
        return trimBST(root.left, low, high);
    } else {
        root.left = trimBST(root.left, low, high);
        root.right = trimBST(root.right, low, high);
    }
    return root;
};

题型二、二叉搜索树的中序遍历

明确中序遍历二叉搜索树等于遍历一个有序数组即可,时间复杂度是 O(n)。

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

image.png

var getMinimumDifference = function(root) {
    let min = Infinity;
    let preVal = undefined;
    const traversal = root => {
        if (!root) return;
        traversal(root.left);
        // 不能写成 if (preVal), 因为 0 是假值, 会产生 bug
        if (preVal !== undefined) {
         min = Math.min(min, root.val - preVal);
       }
        preVal = root.val;
        traversal(root.right);
    }
    traversal(root);
    return min;
};

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

image.png

BST的中序遍历是从小到大,那么反过来就是从大到小,然后累加就好了。

var convertBST = function(root) {
    let sum = 0;
    let convertBST = (root) => {
        if(!root) return;
        convertBST(root.right);
        sum += root.val;
        root.val = sum;
        convertBST(root.left);
    }
    convertBST(root);
    return root;
};