数据结构之二叉搜索树

二叉搜索树

满足条件:

1.对于根节点:左子树中所有节点的值小于右子树中所有节点的值

2.任意节点的左右子树也是二叉搜索树,同样满足条件1

二叉搜索树的常用操作

我们将二叉搜索树封装为一个类 BinarySearchTree ,并声明一个成员变量 root ,指向树的根节点

查找节点

给定目标值target,我们可以根据二叉搜索树的性质来查找,声明一个节点cur从根节点开始遍历

  • cur.val说明targetcur的右子树,执行cur=cur.right
  • cur.val>target说明targetcur的左子树,执行cur=cur.left
  • cur.val=target,返回该节点,跳出循环
/* 查找节点 */
TreeNode *search(int num) {
    TreeNode *cur = root;
    // 循环查找,越过叶节点后跳出
    while (cur != nullptr) {
        // 目标节点在 cur 的右子树中
        if (cur->val < num)
            cur = cur->right;
        // 目标节点在 cur 的左子树中
        else if (cur->val > num)
            cur = cur->left;
        // 找到目标节点,跳出循环
        else
            break;
    }
    // 返回目标节点
    return cur;
}

插入节点

  • 查找插入位置:从根节点出发,根据当前节点值和 num 的大小关系循环向下搜索,直到越过叶节点(遍历至 None )时跳出循环

  • 在该位置插入节点:初始化节点 num ,将该节点置于 None 的位置。

注意:

二叉搜索树中不允许有重复的元素,否则就违反了二叉搜索树的定义,若待插入的节点在二叉搜索树中,则不执行任何操作,直接返回

为了实现插入节点,我们需要借助节点 pre 保存上一轮循环的节点。这样在遍历至 None 时,我们可以获取到其父节点,从而完成节点插入操作

/* 插入节点 */
void insert(int num) {
    // 若树为空,则初始化根节点
    if (root == nullptr) {
        root = new TreeNode(num);
        return;
    }
    TreeNode *cur = root, *pre = nullptr;
    // 循环查找,越过叶节点后跳出
    while (cur != nullptr) {
        // 找到重复节点,直接返回
        if (cur->val == num)
            return;
        pre = cur;
        // 插入位置在 cur 的右子树中
        if (cur->val < num)
            cur = cur->right;
        // 插入位置在 cur 的左子树中
        else
            cur = cur->left;
    }
    // 插入节点
    TreeNode *node = new TreeNode(num);
    if (pre->val < num)
        pre->right = node;
    else
        pre->left = node;
}

删除节点

二叉搜索树的删除分为三种情况

  • 当待删除节点的度为0时,可以直接删除这个节点。
  • 当待删除节点的度为1时,我们将子节点替换待删除的节点即可
  • 当待删除节点的度为2时,我们无法删除这个节点,而是需要一个节点替换这个节点,因为要维持搜索二叉树的性质,所以这个待删除节点的值可以是右子树的最小节点或者左子树的最大节点
/* 删除节点 */
void remove(int num) {
    // 若树为空,直接提前返回
    if (root == nullptr)
        return;
    TreeNode *cur = root, *pre = nullptr;
    // 循环查找,越过叶节点后跳出
    while (cur != nullptr) {
        // 找到待删除节点,跳出循环
        if (cur->val == num)
            break;
        pre = cur;
        // 待删除节点在 cur 的右子树中
        if (cur->val < num)
            cur = cur->right;
        // 待删除节点在 cur 的左子树中
        else
            cur = cur->left;
    }
    // 若无待删除节点,则直接返回
    if (cur == nullptr)
        return;
    // 子节点数量 = 0 or 1
    if (cur->left == nullptr || cur->right == nullptr) {
        // 当子节点数量 = 0 / 1 时, child = nullptr / 该子节点
        TreeNode *child = cur->left != nullptr ? cur->left : cur->right;
        // 删除节点 cur
        if (cur != root) {
            if (pre->left == cur)
                pre->left = child;
            else
                pre->right = child;
        } else {
            // 若删除节点为根节点,则重新指定根节点
            root = child;
        }
        // 释放内存
        delete cur;
    }
    // 子节点数量 = 2
    else {
        // 获取中序遍历中 cur 的下一个节点
        TreeNode *tmp = cur->right;
        while (tmp->left != nullptr) {
            tmp = tmp->left;
        }
        int tmpVal = tmp->val;
        // 递归删除节点 tmp
        remove(tmp->val);
        // 用 tmp 覆盖 cur
        cur->val = tmpVal;
    }
}

你可能感兴趣的:(数据结构,数据结构)