leetcode【数据结构简介】《二叉搜索树》卡片——二叉搜索树中的基本操作

Authur Whywait 做一块努力吸收知识的海绵
想看博主的所有leetcode卡片学习笔记传送门点这儿

  • 了解如何在二叉搜索树中进行搜索、插入或删除;
  • 在二叉搜索树中运用递归或迭代的方法,进行搜索、插入和删除操作;
  • 了解节点数量、树的高度和操作复杂度之间的关系。

搜索操作

在本节中,我们将讨论如何在二叉搜索树中搜索特定的值。

根据BST的特性,对于每个节点:

  • 如果目标值等于节点的值,则返回节点;
  • 如果目标值小于节点的值,则继续在左子树中搜索;
  • 如果目标值大于节点的值,则继续在右子树中搜索。

在这里插入图片描述

二叉搜索树中的搜索🚩

给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。

在这里插入图片描述
返回如下子树:
在这里插入图片描述

分析

由上文中关于BST特性的分析,此题思路显而易见

故而在下面直接贴上代码。

代码实现以及执行结果

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */


struct TreeNode* searchBST(struct TreeNode* root, int 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);
}

在这里插入图片描述

插入操作

二叉搜索树中的另一个常见操作是插入一个新节点。

有许多不同的方法插入新节点,这里我们只讨论一种使整体操作变化最小经典方法

其主要思想是为目标节点找出合适的叶节点位置,然后将该节点作为叶节点插入。 因此,搜索将成为插入的起始。

与搜索操作类似,对于每个节点,我们将:

  • 根据节点值与目标节点值的关系,搜索左子树或右子树;
  • 重复步骤 1 直到到达外部节点;
  • 根据节点的值与目标节点的值的关系,将新节点添加为其左侧或右侧的子节点。

这样,我们就可以添加一个新的节点并依旧维持二叉搜索树的性质。

在这里插入图片描述
与搜索操作相同,我们可以递归或迭代地进行插入。

二叉搜索树中的插入操作🚩

给定二叉搜索树(BST)的根节点和要插入树中的值,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 保证原始二叉搜索树中不存在新值。

注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回任意有效的结果。

分析

如上文所述方法操作即可。

历程

看到题,so easy!

刷刷刷码完代码,然后发现输出的仍为原来的树。代码如下:

struct TreeNode* insertIntoBST(struct TreeNode* root, int val){
    if(!root) return NULL;

    struct TreeNode* temp = root;
    while(temp) temp = temp->val<val ? temp->right : temp->left;
    
    temp = (struct TreeNode *)malloc(sizeof(struct TreeNode));
    temp->val = val;
    temp->left = NULL;
    temp->right = NULL;
    
    return root;
}

王德发?然后各种找啊找,感觉这么“完美”的代码怎么会有错误呢?

这是一条tips:
我们在定义指针的时候,一定要对其初始化!一定要初始化!养成好习惯!

然后去喝了个茶(看了一下别人的代码),发现大家频繁有提到“父结点”这个词。

父结点?和父节点有何关系?

当然有关系啦~

我们跳出while循环的条件是temp为空,然后给temp分配空间,一切看似那么顺理成章。但是我却忽略了一个问题,就是如果temp为NULL,你给他分配了空间,不就是相当于type * a = ()malloc(); temp = a;了吗?

用人话说的话,就是temp此时已经和树这个结构没有任何联系了。

更形象的说,malloc函数带着temp急需的物资(一个指定大小的空间)想接到temp上,认为可以投靠大树抱个大腿,然而temp只是从树顶滑动至树叶最后从叶尖滑下的一滴水珠罢了,他已经和树叶没有一丝联系,至于malloc函数抱大腿的想法,只能是空想罢了。

所以,malloc函数要确定自己和大树之间有没有建成联系,还是要从temp这个水滴接触过的最后一片叶子入手。确定了最后一片叶子,才可以保证自己的投诚(返回相应空间到一个地址)能达到自己预想的效果。

这最后一片叶子,就是他们所谓的父结点

于是,对程序略作修改,即可。

代码实现以及执行结果

struct TreeNode* insertIntoBST(struct TreeNode* root, int val){
    if(!root) return NULL;

    struct TreeNode* temp = root, * father = NULL;
    while(temp){
        father = temp;
        temp = temp->val<val ? temp->right : temp->left;
    }
    temp = (struct TreeNode *)malloc(sizeof(struct TreeNode));
    temp->val = val;
    temp->left = NULL;
    temp->right = NULL;
    
    if(father->val>val) father->left = temp;
    else father->right = temp;
    
    return root;
}

在这里插入图片描述

删除操作

删除要比我们前面提到过的两种操作复杂许多。

有许多不同的删除节点的方法,这篇文章中,我们只讨论一种使整体操作变化最小的方法。

我们的方案是用一个合适的子节点来替换要删除的目标节点。

根据其子节点的个数,我们需考虑以下三种情况:

  1. 如果目标节点没有子节点,我们可以直接移除该目标节点。
  2. 如果目标节只有一个子节点,我们可以用其子节点作为替换。
  3. 如果目标节点有两个子节点,我们需要用其中序后继节点或者前驱节点来替换,再删除该目标节点。

下面是三种情况的演示动态图:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

一帧帧画,一帧帧截图。如果对你理解有帮助,请务必给个👍

删除二叉搜索树中的结点🚩

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

分析

本来我使用迭代法,代码又长,情况还多,吐血三升。

所以半路出家,决定使用递归法。

下面为介绍。

主要步骤为两步:搜索 & 删除

关于搜索:搜索不到就返回原root,否则执行删除操作

关于删除:分为两种情况

  1. 如果右子树为空,则返回左子树的root;
  2. 如果右子树非空,找到其顺序后继,对指来指去的指针处理好了之后,返回其顺序后继。

动态演示

说实话,直接阅读代码不如给你看动态图,了解具体操作步骤之后,用代码实现操作步骤是迟早的事。

于是我们直接从右子树不为空的情况开始。

先是比较简单的情况,root的子树直接为其顺序后继。
在这里插入图片描述

再是稍微复杂一些的,右子树的根结点不是顺序后继的情况。
在这里插入图片描述

一帧帧画图,如果顺序错了,或者哪里写错了没注意到就要重新回到原处重新画,画完再一帧帧截图。如此反复四五次之后,我决定不再修改了。虽然里面有非常小的偏差(和后面附上的代码),但是并不影响读者了解具体操作步骤。点个👍支持下呗o( ̄▽ ̄)o

代码实现以及执行结果

struct TreeNode* deleteNode(struct TreeNode* root, int key){
    if(!root) return root;
    if(key > root->val) root->right = deleteNode(root->right, key);
    else if(key < root->val) root->left = deleteNode(root->left, key);
    else{
        if(!root->right) return root->left;
        else{
            struct TreeNode* cur = root->right, * pre = NULL;
            while(cur->left){
                pre = cur;
                cur = cur->left;
            }
            
            if(pre) pre->left = NULL;
            cur->left = root->left;

            if(pre){
                struct TreeNode* rightCur = cur;
                while(rightCur->right) rightCur = rightCur->right;
                rightCur->right = root->right;
            }
            return cur;
        }
    }
    return root;
}

在这里插入图片描述

都看到这里了,确定不点个赞再走?(╯▔皿▔)╯

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AuthurLEE

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值