[LeetCode] Delete Node in a BST 删除二叉搜索树中的节点

简介:

Given a root node reference of a BST and a key, delete the node with the given key in the BST. Return the root node reference (possibly updated) of the BST.

Basically, the deletion can be divided into two stages:

  1. Search for a node to remove.
  2. If the node is found, delete the node.

Note: Time complexity should be O(height of tree).

Example:

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

    5
   / \
  3   6
 / \   \
2   4   7

Given key to delete is 3. So we find the node with value 3 and delete it.

One valid answer is [5,4,6,2,null,null,7], shown in the following BST.

    5
   / \
  4   6
 /     \
2       7

Another valid answer is [5,2,6,null,4,null,7].

    5
   / \
  2   6
   \   \
    4   7

这道题让我们删除二叉搜索树中的一个节点,这道题的难点在于删除完节点并补上那个节点的位置后还应该是一棵二叉搜索树。被删除掉的节点位置,不一定是由其的左右子节点补上,比如下面这棵树:

         7
        / \
       4   8
     /   \   
    2     6
     \   /
      3 5

如果我们要删除节点4,那么应该将节点5补到4的位置,这样才能保证还是BST,那么结果是如下这棵树:

         7
        / \
       5   8
     /   \   
    2     6
     \   
      3

我们先来看一种递归的解法,首先判断根节点是否为空。由于BST的左<根<右的性质,使得我们可以快速定位到要删除的节点,我们对于当前节点值不等于key的情况,根据大小关系对其左右子节点分别调用递归函数。若当前节点就是要删除的节点,我们首先判断是否有一个子节点不存在,那么我们就将root指向另一个节点,如果左右子节点都不存在,那么root就赋值为空了,也正确。难点就在于处理左右子节点都存在的情况,我们需要在右子树找到最小值,即右子树中最左下方的节点,然后将该最小值赋值给root,然后再在右子树中调用递归函数来删除这个值最小的节点,参见代码如下:

解法一:

class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        if (!root) return NULL;
        if (root->val > key) {
            root->left = deleteNode(root->left, key);
        } else if (root->val < key) {
            root->right = deleteNode(root->right, key);
        } else {
            if (!root->left || !root->right) {
                root = (root->left) ? root->left : root->right;
            } else {
                TreeNode *cur = root->right;
                while (cur->left) cur = cur->left;
                root->val = cur->val;
                root->right = deleteNode(root->right, cur->val);
            }
        }
        return root;
    }
};

下面我们来看迭代的写法,还是通过BST的性质来快速定位要删除的节点,如果没找到直接返回空。遍历的过程要记录上一个位置的节点pre,如果pre不存在,说明要删除的是根节点,如果要删除的节点在pre的左子树中,那么pre的左子节点连上删除后的节点,反之pre的右子节点连上删除后的节点。在删除函数中,如果左右子节点都不存在,那么返回空;如果有一个不存在,那么我们返回那个存在的;难点还是在于处理左右子节点都存在的情况,还是要找到需要删除节点的右子树中的最小值,然后把最小值赋值给要删除节点,然后就是要处理最小值可能存在的右子树的连接问题,如果要删除节点的右子节点没有左子节点了的话,那么最小值的右子树直接连到要删除节点的右子节点上即可(因为此时原本要删除的节点的值已经被最小值替换了,所以现在其实是要删掉最小值节点)。否则我们就把最小值节点的右子树连到其父节点的左子节点上。文字表述确实比较绕,请大家自行带例子一步一步观察就会很清晰明了了,参见代码如下:

解法二:

class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        TreeNode *cur = root, *pre = NULL;
        while (cur) {
            if (cur->val == key) break;
            pre = cur;
            if (cur->val > key) cur = cur->left;
            else cur = cur->right;
        }
        if (!cur) return root;
        if (!pre) return del(cur);
        if (pre->left && pre->left->val == key) pre->left = del(cur);
        else pre->right = del(cur);
        return root;
    }
    TreeNode* del(TreeNode* node) {
        if (!node->left && !node->right) return NULL;
        if (!node->left || !node->right) {
            return (node->left) ? node->left : node->right;
        }
        TreeNode *pre = node, *cur = node->right;
        while (cur->left) {
            pre = cur;
            cur = cur->left;
        }
        node->val = cur->val;
        (pre == node ? node->right : pre->left) = cur->right;
        return node;
    }
};

下面来看一种对于二叉树通用的解法,适用于所有二叉树,所以并没有利用BST的性质,而是遍历了所有的节点,然后删掉和key值相同的节点,参见代码如下:

解法三:

class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        if (!root) return NULL;
        if (root->val == key) {
            if (!root->right) return root->left;
            else {
                TreeNode *cur = root->right;
                while (cur->left) cur = cur->left;
                swap(root->val, cur->val);
            }
        }
        root->left = deleteNode(root->left, key);
        root->right = deleteNode(root->right, key);
        return root;
    }
};

本文转自博客园Grandyang的博客,原文链接:删除二叉搜索树中的节点[LeetCode] Delete Node in a BST ,如需转载请自行联系原博主。

相关文章
|
Go 索引 Perl
【LeetCode 热题100】【二叉树构造题精讲:前序 + 中序建树 & 有序数组构造 BST】(详细解析)(Go语言版)
本文详细解析了二叉树构造的两类经典问题:通过前序与中序遍历重建二叉树(LeetCode 105),以及将有序数组转化为平衡二叉搜索树(BST,LeetCode 108)。文章从核心思路、递归解法到实现细节逐一拆解,强调通过索引控制子树范围以优化性能,并对比两题的不同构造逻辑。最后总结通用构造套路,提供进阶思考方向,帮助彻底掌握二叉树构造类题目。
1103 9
【LeetCode 45】701.二叉搜索树中的插入操作
【LeetCode 45】701.二叉搜索树中的插入操作
211 1
【LeetCode 48】108.将有序数组转换为二叉搜索树
【LeetCode 48】108.将有序数组转换为二叉搜索树
230 0
【LeetCode 47】669.修剪二叉搜索树
【LeetCode 47】669.修剪二叉搜索树
169 0
【LeetCode 46】450.删除二叉搜索树的节点
【LeetCode 46】450.删除二叉搜索树的节点
273 0
|
JavaScript Unix Linux
nvm与node.js的安装指南
通过以上步骤,你可以在各种操作系统上成功安装NVM和Node.js,从而在不同的项目中灵活切换Node.js版本。这种灵活性对于管理不同项目的环境依赖而言是非常重要的。
3926 11
|
存储 JavaScript 搜索推荐
Node框架的安装和配置方法
安装 Node 框架是进行 Node 开发的第一步,通过正确的安装和配置,可以为后续的开发工作提供良好的基础。在安装过程中,需要仔细阅读相关文档和提示,遇到问题及时解决,以确保安装顺利完成。
1322 155
|
弹性计算 JavaScript 前端开发
一键安装!阿里云新功能部署Nodejs环境到ECS竟然如此简单!
Node.js 是一种高效的 JavaScript 运行环境,基于 Chrome V8 引擎,支持在服务器端运行 JavaScript 代码。本文介绍如何在阿里云上一键部署 Node.js 环境,无需繁琐配置,轻松上手。前提条件包括 ECS 实例运行中且操作系统为 CentOS、Ubuntu 等。功能特点为一键安装和稳定性好,支持常用 LTS 版本。安装步骤简单:登录阿里云控制台,选择扩展程序管理页面,安装 Node.js 扩展,选择实例和版本,等待创建完成并验证安装成功。通过阿里云的公共扩展,初学者和经验丰富的开发者都能快速进入开发状态,开启高效开发之旅。
|
资源调度 JavaScript 前端开发
前端开发必备!Node.js 18.x LTS保姆级安装教程(附国内镜像源配置)
本文详细介绍了Node.js的安装与配置流程,涵盖环境准备、版本选择(推荐LTS版v18.x)、安装步骤(路径设置、组件选择)、环境验证(命令测试、镜像加速)及常见问题解决方法。同时推荐开发工具链,如VS Code、Yarn等,并提供常用全局包安装指南,帮助开发者快速搭建高效稳定的JavaScript开发环境。内容基于官方正版软件,确保合规性与安全性。
16385 23

热门文章

最新文章