前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >二叉搜索树删除节点 动画演示

二叉搜索树删除节点 动画演示

作者头像
double
发布2020-08-11 15:23:24
1K0
发布2020-08-11 15:23:24
举报
文章被收录于专栏:算法channel算法channel

Day60:删除二叉搜索树的某个节点

1 题目

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

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

  • 首先找到需要删除的节点;
  • 如果找到了,删除它。

说明:要求算法时间复杂度为 O(h),h 为树的高度。

示例:

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

key = 3

代码语言:javascript
复制
    5
   / \
  3   6
 / \   \
2   4   7

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

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

代码语言:javascript
复制
    5
   / \
  4   6
 /     \
2       7

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

代码语言:javascript
复制
    5
   / \
  2   6
   \   \
    4   7

2 分析过程

这道题被leetcode定为中等难度级别,实话讲确实属于这类级别,虽然思路不难,但是要想一次写出准确无误的代码,仍然不是一件简单的事情。如果你第一次做这道题能很快写出来,说明你对递归的理解、指针的掌控都达到了一定水平。

这道题的思路很straitforward,根据BST的性质,具体说来分为如下几种情况:

  1. 如果被删除节点关键码key小于当前根节点nodei.val,则问题规模直接降阶到左子树中
  2. 关键码key大于当前根节点nodei.val,直接到右子树中查找
  3. key等于当前根节点nodei.val,又分为4种情况: (1). nodei 无左右子树,摘除nodei节点,等于直接返回 None (2). nodei 仅有左子树,摘除nodei节点,等于直接返回 nodei.left (3). nodei 仅有右子树,摘除nodei节点,等于直接返回 nodei.right (4). nodei 都有左右子树,麻烦一点,方法之一:选择以nodei.right为根节点的树中最左侧节点,然后替换nodei

最后返回 nodei.

你看,上面的思路应该足够清晰,但是兑现为代码绝对又是另一回事。你首先要对递归有深刻的理解,其次像链表、二叉树等这类具备递归的数据结构,操作它们节点引用问题要时刻保持清醒,很容易出错。

3 如何写出代码

老规矩,这是我们的节点定义:

代码语言:javascript
复制
# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

解决方案对象的主方法,调用自定义方法self.__delNodei(root,key),这个方法的构思思路是这样:

第一个参数是BST树中的任意节点,因为BST严格满足递归,所以选取任意一个以节点nodei为根的树,删除里面等于key的节点。因此,这个功能一旦实现后,只需在参数赋值时赋值它为root即可。

代码语言:javascript
复制
class Solution(object):
    def deleteNode(self, root, key):
        """
        :type root: TreeNode
        :type key: int
        :rtype: TreeNode
        """
        return self.__delNodei(root,key)

所以关键是如何写出def __delNodei(self,nodei,key)方法,下面我们一步一步分析。

根据上面的4种情况分析,我们在情况4时会用到找树的最左节点,为此先写出这个方法def __findMinNode(self,nodei)

代码语言:javascript
复制
    # 找到以nodei为根节点树的最小值
    def __findMinNode(self,nodei):
        if not nodei.left:
            return nodei
        while nodei.left:
            nodei = nodei.left
        return nodei

这个比较简单,是链表、二叉树等递归结构的常规迭代思路,注意与向量表i=i+1迭代思路的区别。

先写出第一种大情况,比较简单。因为方法self.__delNodei(nodei.left,key)实现删除等于key的节点后返回对nodei.left节点的引用,所以将nodei的left域指向它即可。

代码语言:javascript
复制
    # 假定已经查询到nodei节点,删除后返回nodei节点的引用
    def __delNodei(self,nodei,key):
        if not nodei:
            return None

        #若满足下面条件,一定在左子树
        if key < nodei.val:
            nodei.left = self.__delNodei(nodei.left,key) # 删除后返回nodei.left节点的引用   

以下面二叉搜索树删除值等于3的节点为例演示,伸入到左子树:

再写出第二种大情况,与上类似:

代码语言:javascript
复制
# 一定在右子树
        elif nodei.val < key:
            nodei.right = self.__delNodei(nodei.right,key)# 删除后返回nodei.right节点的引用

再写出第三种大情况,即找到了等于key节点,又分四种小情况:

被删除节点是叶子节点:直接返回None,就是摘除它了:

代码语言:javascript
复制
 # nodei.val== key,删除nodei
        else:
            # 情况1:被删除节点是叶子节点
            if not nodei.left and not nodei.right:
                return None  # 置为None

第二、三种小情况很相似,跳过被删除节点nodei,直接返回nodei.left 或 node.right 即可:

代码语言:javascript
复制
            # 情况2:被删除节点无右子树
            if not nodei.right:
                return nodei.left # 跳过nodei,返回指向nodei.left,相当于删除了nodei
            # 情况3:被删除节点无左子树
            if not nodei.left:
                return nodei.right

最后一种小情况,大家直接看注释吧:

代码语言:javascript
复制
            # 情况4:被删除节点左右子树都不为空
            # 先找到右子树中最左节点,即右子树最小值节点
            minNodei = self.__findMinNode(nodei.right)
            nodei.val = minNodei.val
            # 删除minNodei,同时返回nodei.right的引用,并赋值给nodei.right
            ## 切记:key已经不是__delNodei的参数key,而是我们找到的minNodei的val值
            nodei.right = self.__delNodei(nodei.right,minNodei.val) 
        
        return nodei

上面代码,删除节点3的动画演示:

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-08-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员郭震zhenguo 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Day60:删除二叉搜索树的某个节点
    • 1 题目
      • 2 分析过程
        • 3 如何写出代码
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档