二叉搜索树

二叉搜索树(Binary Search Tree,BST),又称为二叉排序树,它通过二叉树将数据组织起来。

树的每个节点都包含了键、数据、左子节点指针、右子节点指针和父节点指针,其中键是最核心的部分,键的值决定了树的组织形状。

举个例子:身份证号为键,姓名为数据,通过身份证号查找姓名。左子节点指针指向左子节点,而右子节点指针指向右子节点。

二叉搜索树能够保证键的有序排列,所以查找时使用的是二分查找方式。每次查找都从二叉搜索树的根节点开始,通过比较键的大小来决定从左边还是右边进行查找,直到到达叶子节点。其实这个过程可以看成是每次选择树的其中一半进行查找而跳过另外一半。这是一种较为高效的查找方式。

性质

  • 二叉搜索树的左右子树也分别是二叉搜索树。
  • 二叉搜索树的左子树中的所有节点的键都小于它的根节点的键。
  • 二叉搜索树的右子树中的所有节点的键都大于它的根节点的键。
  • 二叉搜索树可以是一棵空树。
  • 一般来说,树中的每个节点的键都不相等,但根据需要也可以将相同的键插入到树中。

插入操作

插入时主要耗时在查找操作上,它的平均时间复杂度为O(log n)。插入操作的要点如下。

  • 如果为空树,则将插入的节点作为根节点。
  • 如果不为空树,则从根节点开始比较。先比较插入节点与根节点的键,如果二者值相同,则不做任何处理直接返回,而如果插入节点大于根节点,则继续与所比较节点的右子节点比较,反之则继续与所比较节点的左子节点比较。
  • 如果插入节点大于根节点,则把根节点的右子节点与插入节点进行比较。如果插入节点的键大于右子节点的键,则继续与右子节点的右子节点进行比较;如果插入节点的键小于右子节点的键,则继续与右子节点的左子节点进行比较。
  • 以此类推,不断往下查找,直到找到空的待比较节点,将插入节点放进去。

查找操作

查找操作的平均时间复杂度为O(log n),其要点如下。

  • 从根节点开始,比较查找节点与根节点的键的大小。如果两者相等,则表示已找到该节点,直接返回。如果查找节点大于根节点,则继续往右子节点查找,反之,则继续往左子节点查找。
  • 如果查找节点大于根节点,则让根节点的右子节点与查找节点进行比较。如果查找节点的键较大,则继续往右子节点的右子节点查找,反之,则继续往右子节点的左子节点查找。
  • 以此类推,不断往下查找,直到找到某个节点的键与查找节点的相同,则表示查找成功。如果直到空节点都找不到,则说明不存在该节点。

中序前驱节点

通过某种方式遍历二叉搜索树后会得到一个序列结果,而某个节点的前驱节点就是在该序列中此节点的前一个节点。

由于中序遍历所得到的序列是按键的值从小到大排列的,所以在中序遍历中,某节点的前驱节点就是小于该节点的所有节点中最大的那个节点,比如1、2、3、4、5中的2就是3的前驱节点。

查找中序前驱节点就是查找小于某个节点的所有节点中的最大节点,主要分为以下三种情况。
❖ 情况1
如果某个节点存在左子节点,那么左子节点(子树)下的最大节点即是该节点的前驱节点。
① 假如我们要查找节点C的前驱节点,则先找到节点C。
② 此时因为存在左子节点A,所以我们只要找到节点A下的最大节点即可。
③ 最终找到节点B是最大节点,即节点B是节点C的前驱节点。
在这里插入图片描述
❖情况2
如果某个节点没有左子节点,而且该节点为其父节点的右子节点,那么该节点的父节点即为该节点的前驱节点。
① 假如我们准备查找节点D的前驱节点。
② 因为节点D没有左子节点,且节点D为节点C的右子节点,所以节点C即是节点D的前驱节点。
在这里插入图片描述
❖ 情况3
如果某个节点没有左子节点,而且该节点为其父节点的左子节点,那么就往顶端查找,直到找到一个节点是其父节点的右子节点,则该节点的父节点就是要找的前驱节点。我们看看下面这种情况。
① 假如我们准备查找节点F的前驱节点。
② 因为节点F没有左子节点,且节点F为其父节点G的左子节点,于是继续往顶端查找。
③ 此时,因为节点G是其父节点E的右子节点,所以节点G的父节点就是节点下的前驱节点,即节点E。
在这里插入图片描述

中序后继节点

与前驱节点相对应的是后继节点,中序遍历二叉搜索树后会得到一个序列结果,这时某个节点的后继节点就是在该序列中此节点的后一个节点。由于中序遍历所得到的序列是按键的值从小到大排列的,所以某节点的后继节点就是大于该节点的所有节点中最小的那个节点,比如1、2、3、4、5中的4就是3的后继节点。

查找中序后继节点就是查找大于某个节点的所有节点中的最小节点,同样分以下三种情况。
❖ 情况1
如果某个节点存在右子节点,那么右子节点(子树)下的最小节点是该节点的后继节点。
① 假如我们要查找节点E的后继节点,则先找到节点E。
② 因为节点E存在右子节点G,所以只要找到节点G下的最小节点即可。
③ 最终找到节点F是最小节点,它就是节点E的后继节点。
在这里插入图片描述
❖ 情况2
如果某个节点没有右子节点,而且该节点为其父节点的左子节点,那么该节点的父节点即为该节点的后继节点。
① 假如我们要找节点F的后继节点。
② 因为节点F没有右子节点,且节点F为节点G的左子节点,所以节点G是节点F的后继节点。
在这里插入图片描述
❖ 情况3
如果某个节点没有右子节点,而且该节点为其父节点的右子节点,那么就往顶端查找,直到找到一个节点是其父节点的左子节点,则该节点的父节点就是要找的后继节点。
① 假如我们要找节点B的后继节点。
② 因为节点B没有右子节点,且节点B为其父节点的右子节点,于是往顶端查找。
③ 此时因为节点A是其父节点C的左子节点,于是节点A的父节点即为要找的后继节点,即节点C。
在这里插入图片描述

删除操作

删除操作的平均时间复杂度为O(log n)。删除操作分以下三种情况进行。
如果被删除的节点为叶子节点,即它的左子节点指针和右子节点指针都为空时,则可以直接删掉该节点,这并不会破坏二叉搜索树的性质。
如果被删除的节点只有一个子节点(左子节点或右子节点),则直接将子节点上移到被删除节点的位置。
如果被删除的节点有两个子节点,此时需要找到被删除节点的中序后继节点或中序前驱节点来替换被删除节点。

❖ 情况1
假如要删除叶子节点B,则执行步骤如下。
① 从根节点开始查找。
② 由于B小于E,继续与C比较。
③ 由于B小于C,继续与A比较。
④ 由于B大于A,往右子节点查找,找到要删除的节点B。
⑤ 因为B是叶子节点,直接删除即可。
在这里插入图片描述
❖ 情况2
① 假如树的结构如下图所示。
② 现在要删除节点C,从根节点开始查找。
③ 由于C小于E,继续往左子节点查找,此时发现已找到节点C。
④ 因为节点C只有一个子节点,所以直接将原来指向节点C的节点E的左子节点指针指向C的子节点A。
⑤ 删除后得到最终的二叉搜索树结构。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
❖ 情况3
① 现准备删除树中的节点E,先从根节点开始查找节点E。
② 由于节点E存在两个子节点,于是查找节点E的中序前驱节点来替换它,其中序前驱节点为左子节点C下的最大节点。
③ 要找节点C下的最大节点,则一直往右查找,直到不能继续往下,所以节点D即节点E的中序前驱节点。
④ 接着用节点D来替换节点E,最终将节点E删除。
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

DoWayChan

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

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

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

打赏作者

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

抵扣说明:

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

余额充值