二叉排序树的删除

2023-05-16

(网上讲二叉排序树删除的资料很少,这篇很不错!转自:http://bbs.csdn.net/topics/110010437)

二叉排序树的删除
      对于一般的二叉树来说,删去树中的一个结点是没有意义的,因为它将使以被删除的结点为根的子树变成森林,破坏了整棵树的结构
但是,对于二叉排序树,删去树上的一个结点相当于删去有序序列中的一个记录,只要在删除某个结点后不改变二叉排序树的特性即可。
      在二叉排序树上删除一个结点的算法如下:

btree * DeleteBST(btree *b, ElemType x)
{
      if (b)
      {
            if (b->data == x)
                  b = DelNode(b);
            else if (b->data > x)
                  b->lchild = DeleteBST(b->lchild, x);
            else
                  b->rchild = DeleteBST(b->rchild, x);
      }
      return b;
}
 

其中删除过程有两种方法。
第一种过程如下:
1。若p有左子树,找到其左子树的最右边的叶子结点r,用该叶子结点r来替代p,把r的左孩子
作为r的父亲的右孩子。
2。若p没有左子树,直接用p的右孩子取代它。

第二种过程如下:
1。若p有左子树,用p的左孩子取代它;找到其左子树的最右边的叶子结点r,把p的右子树作为r
的右子树。
2。若p没有左子树,直接用p的右孩子取代它。
    两种方法各有优劣,第一种操作简单一点点,但均衡性不如第二种,因为它将结点p的右子树
全部移到左边来了。下面将分别以两种种思路编写代码。
第一种:

btree * DelNode(btree *p)
{
      if (p->lchild)
      {
            btree *r = p->lchild;   //r指向其左子树;
        while(r->rchild != NULL)//搜索左子树的最右边的叶子结点r
        {
            r = r->rchild;
        }
            r->rchild = p->rchild;

            btree *q = p->lchild;   //q指向其左子树;
            free(p);
            return q;
      }
      else
      {
            btree *q = p->rchild;   //q指向其右子树;
            free(p);
            return q;
      }
}
第二种:
btree * DelNode(btree *p)
{
      if (p->lchild)
      {
            btree *r = p->lchild;   //r指向其左子树;
            btree *prer = p->lchild;   //prer指向其左子树;
        while(r->rchild != NULL)//搜索左子树的最右边的叶子结点r
        {
                  prer = r;
            r = r->rchild;
        }

        if(prer != r)//若r不是p的左孩子,把r的左孩子作为r的父亲的右孩子
        {
                  prer->rchild = r->lchild;
                  r->lchild = p->lchild; //被删结点p的左子树作为r的左子树
            }
        r->rchild = p->rchild; //被删结点p的右子树作为r的右子树

            free(p);
            return r;
      }
      else
      {
            btree *q = p->rchild;   //q指向其右子树;
            free(p);
            return q;
      }
}

    但是上面这种方法,把r移来移去,很容易出错,其实在这里我们删除的只是p的元素值,而不是它的地址,所以完全没有必要移动指针。仔细观察,发现我们删除的地址实际上是p的左子树的最右边的叶子结点r的地址,所以我们只要把r的数据填到p中,然后把r删除即可。
算法如下:
btree * DelNode(btree *p)
{
      if (p->lchild)
      {
            btree *r = p->lchild;   //r指向其左子树;
            btree *prer = p->lchild;   //prer指向其左子树;
        while(r->rchild != NULL)//搜索左子树的最右边的叶子结点r
        {
                  prer = r;
            r = r->rchild;
        }
            p->data = r->data;

        if(prer != r)//若r不是p的左孩子,把r的左孩子作为r的父亲的右孩子
                  prer->rchild = r->lchild;
            else
            p->lchild = r->lchild; //否则结点p的左子树指向r的左子树

            free(r);
            return p;
      }
      else
      {
            btree *q = p->rchild;   //q指向其右子树;
            free(p);
            return q;
      }
}

 http://www.programfan.com/

——————————————————————————————————————————————————————————————————

1.二叉排序树的概念:
  二叉排序树是一种动态树表。
   二叉排序树的定义:二叉排序树或者是一棵空树,
   或者是一棵具有如下性质的二叉树:
     ⑴ 若它的左子树非空,则左子树上所有结点的值均小于根结点的值;
     ⑵ 若它的右子树非空,则右子树上所有结点的值均大于根结点的值;
     ⑶ 左、右子树本身又各是一棵二叉排序树。二叉排序树的性质: 按中序遍历二叉排序树,所得到的中序遍历序列是一个递增有序序列

2.二叉排序树的插入:
   在二叉排序树中插入新结点,要保证插入后的二叉树仍符合二叉排序树的定义。
   插入过程:若二叉排序树为空,则待插入结点*S作为根结点插入到空树中;
   当非空时,将待插结点关键字S->key和树根关键字t->key进行比较,
   若s->key = t->key,则无须插入,若s->key< t->key,则插入到根的左子树中,
   若s->key> t->key,则插入到根的右子树中。而子树中的插入过程和在树中的插入过程相同,
   如此进行下去,直到把结点*s作为一个新的树叶插入到二叉排序树中,或者直到发现树已有相同关键字的结点为止。

3. 二叉排序树生成:
   从空的二叉排序树开始,经过一系列的查找插入操作以后,生成了一棵二叉排序树。
   说明:
     ① 每次插入的新结点都是二叉排序树上新的叶子结点。
     ② 由不同顺序的关键字序列,会得到不同二叉排序树。
     ③ 对于一个任意的关键字序列构造一棵二叉排序树,其实质上对关键字进行排序。

4.二叉排序树查找的程序实现:
 #include<malloc.h>
#include<iostream.h>
#include<stdio.h>
typedef struct BiTNode{
 int data;
 int flag;
 struct BiTNode *lchild,*rchild;
}BTNode,BTree;

//二叉排序树的查找非递归算法
//在二叉排序树T中查找关键字为key的元素,若找到返回true,
//否则返回false.
bool SearchBST(BTree *T,int key){
 BTree *p=T;
 while(p){
  if(p->data==key)
   return true;  
  p=(key<p->data)? p->lchild:p->rchild;
 }
 return false;
}

//二叉排序树的查找递归算法
//在二叉排序树T中查找关键字为key的元素,若找到返回true,
//否则返回false.
bool SearchBST2(BTree *T,int key){
 BTree *p=T;
 if(!p)
  return false;
 else 
  if(p->data==key)
   return true;
  else 
   if(key>p->data)
    return SearchBST2(p->rchild,key);
   else
    return SearchBST2(p->lchild,key);
}

//建立二叉排序树
//当二叉排序树T中不存在关键字为key的元素时,插入key,并返回树的根,
//否则不插入,并返回树的根。   
BTree* InsertBST(BTree *T,int key){
 BTree *f=T,*p=T;
 while(p){
  if(p->data==key) return T;
  f=p;//用f记下查找路径上的最后一个访问的节点
  p=(key<p->data)? p->lchild:p->rchild;
 }
 p=(BTNode*)malloc(sizeof(BTNode));
 p->data=key;
 p->lchild=p->rchild=NULL;
 if(T==NULL) 
  T=p;
 else 
  if (key<f->data)
   f->lchild=p;
  else 
   f->rchild=p;
 return T;
}

//递归中序遍历
void InOrderDisplay(BTree *T){
 if(T){  
  InOrderDisplay(T->lchild);
  cout<<T->data;
  InOrderDisplay(T->rchild);
 }
}

//test:
int main(){
 int i;
 int data;
 BTree *tree=NULL;
 for(i=0;i<4;i++){
  cout<<"input data"<<endl;
  cin>>data;
  tree=InsertBST(tree,data);
 }
 InOrderDisplay(tree);
 bool find=SearchBST2(tree,24);
 cout<<find<<endl;
 return 0;
}

5. 二叉排序树的删除:
  
假设被删结点是*p,其双亲是*f,不失一般性,设*p是*f的左孩子,下面分三种情况讨论:
⑴ 若结点*p是叶子结点,则只需修改其双亲结点*f的指针即可。
⑵ 若结点*p只有左子树PL或者只有右子树PR,则只要使PL或PR 成为其双亲结点的左子树即可。
⑶ 若结点*p的左、右子树均非空,先找到*p的中序前趋结点*s(注意*s是*p的左子树中的最右下的结点,它的右链域为空),然后有两种做法:
① 令*p的左子树直接链到*p的双亲结点*f的左链上,而*p的右子树链到*p的中序前趋结点*s的右链上。
② 以*p的中序前趋结点*s代替*p(即把*s的数据复制到*p中),将*s的左子树链到*s的双亲结点*q的左(或右)链上。(在下面的演示算法中用的就是此方法)


6. 删除算法演示 :

//删除二叉排序树中的一个节点
//在二叉排序树T中删除关键字为key的结点

void DelBST(BTree *T,int key){
 BTree *p=T,*f,*q,*s,*root=T;
 while(p){
  if(p->data==key) break;
 //找到关键字为key的结点
        f=p
;//记下关键字key节点的父节点
  p=(key<p->data)?p->lchild:p->rchild;//分别在*p的左、右子树中查找
 }
 if(!p) return;//
二叉排序树中无关键字为key的结点
 if(p->lchild==NULL&&p->rchild==NULL){//p没有左右子树
  if(p==T) T=NULL;//删除的是根节点
  else
   if(p==f->lchild)
    f->lchild=NULL;
   else
    f->rchild=NULL;
 free(p);
 }
 else if(p->lchild==NULL&&p->rchild!=NULL)//p无左子树有右子树
 { 
  if(f->lchild==p)
   f->lchild=p->rchild; //将p的右子树链接到其父结点的左链上
  else
   f->rchild=p->rchild; //将p的右子树链接到其父结点的右链上
  free(p);
 }
    else if(p->rchild==NULL&&p->lchild!=NULL)//p有左子树无右子树
 { 
  if (f->lchild==p)               
   f->lchild=p->lchild; //将p的左子树链接到其父结点的左链上
  else
   f->rchild=p->lchild; //将p的左子树链接到其父结点的右链上
  free(p);
 }
 else if(p->lchild!=NULL&&p->rchild!=NULL)//p既有左子树又有右子树
 {
  q=p;
  s=p->lchild;//转左
  while(s->rchild){//然后向右到尽头
   q=s;
   s=s->rchild;//s指向被删节点的“前驱”(中序前驱)
  }
  p->data=s->data;//以p的中序前趋结点s代替p(即把s的数据复制到p中)
  if(q!=p) q->rchild=s->lchild;//重接q的右子树
  else q->lchild=s->lchild;//重接q的左子树。
  free(s);
    }
}

7. 二叉排序树的查找:
  在二叉排序树中进行查找的过程和二分查找类似,也是一个逐步缩小查找范围的过程。若查找成功,则是走了一条从根结点到待查结点的路径;若查找失败,则是走了一条根结点到某个叶子结点的路径。因此,查找过程中和关键字比较的次数不超过树的深度。
   由于含有n个结点的二叉排序树不唯一,形态和深度可能不同。故含有n个结点的二叉排序树的平均查找长度和树的形态有关。
   最好的情况是: 二叉排序树和二叉判定树形态相同。
   最坏的情况是: 二叉排序树为单支树,这时的平均查找长度和顺序查找时相同。
   最坏情况示例 
   就平均性能而言,二叉排序树上的查找和二分查找相差不大,并且二叉排序树上的插入和删除结点十分方便,无须大量移动结点。  


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

二叉排序树的删除 的相关文章

  • 七、输入/输出流--streambuffer类介绍--

    缓冲区类 类模板定义为basic streambuf xff0c 由 lt iostream gt 给出 xff1a 1 stream缓冲区 通常stream不负责实际读写操作 xff0c 而是stream buffer实现streambu
  • 七、输入/输出流--streambuffer类介绍--自定义缓冲区

    基本上没看懂 xff0c 那个大神如果可以的话 xff0c 推荐一点相关资料 xff0c 真的不太明白这个缓冲区的内部原理 3 自定义缓冲区 缓冲区有basic streambuf定义 xff0c 针对字型为char和wchar标准库提供了
  • 串口通信学习(GPS模块)2021.5.10

    GPS串口通信学习实践 2021 5 10 1 串口通信简介1 1 波特率1 2 数据位1 3 停止位1 4 奇偶校验位 2 GPS模块串口通信配置2 1 驱动安装2 2 插入GPS模块2 3 GPS模块串口通信数据简介 3 Java实现G
  • Lambda表达式的使用

    对于jdk1 8其实并不是那么熟悉 xff0c 但是要学习这一点 xff0c 对以后工作有好处 xff0c 接下来开始学习jdk1 8在Android studio的配置以及lambda表达式的使用吧 Lambda表达式 jdk1 8中新增
  • ROS实现无人机目标跟踪/物体跟随/循迹

    无人机自主物体跟随 循迹 1 物体跟踪1 1 实现思路1 2 代码示例 2 自主寻线 本实验采用ROS和OpenCV实现功能 xff0c 实验平台采用Parrot的Bebop2无人机ROS部分的学习可以参考我的专栏 xff1a ROS学习记
  • vs code中项目的基本配置--include路径、运行参数、debug配置

    1 安装C C 43 43 for Visual Studio Code 点击左边扩展栏图标 gt 搜索C C 43 43 gt 安装 gt Reload xff1a 安装完成之后 xff0c 打开你的包含c 43 43 的文件夹 xff0
  • CMakeLists.txt编写规范模板及CMake常用变量

    文章目录 基本语法规则常见CMakeLists txt中指令剖析从VS项目配置过程理解CMakeLists内容CMake中常用变量汇总常用CMakeLists文件模板 基础模板使用OpenCV库CMakeLists文件模板使用PCL库CMa
  • C++ 多线程detach()操作的坑以及传参

    detach 的作用是将子线程和主线程的关联分离 xff0c 也就是说detach 后子线程在后台独立继续运行 xff0c 主线程无法再取得子线程的控制权 xff0c 即使主线程结束 xff0c 子线程未执行也不会结束 当主线程结束时 xf
  • 条件变量中的唤醒丢失问题分析

    本文是在其他作者博文的基础上进行了部分补充 原文 xff1a https zhuanlan zhihu com p 55123862 0 前言 条件变量 xff08 condition variable xff09 和互斥锁 xff08 m
  • 无人车传感器 IMU与GPS数据融合进行定位机制

    前言 上一次的分享里 xff0c 我介绍了GPS的原理 xff08 三角定位 xff09 及特性 xff08 精度 频率 xff09 xff0c 同时也从无人车控制的角度 xff0c 讨论了为什么仅有GPS无法满足无人车的定位要求 为了能让
  • C++类对象的赋值与=运算符重载

    本文主要介绍C 43 43 中的赋值运算符重载函数 xff08 operator 61 xff09 的相关知识 1 概述 1 1 why 首先介绍为什么要对赋值运算符 61 进行重载 某些情况下 xff0c 当我们编写一个类的时候 xff0
  • 微信小程序开发入门(一)

    所有示例的完整代码 xff0c 都可以从 GitHub 的代码仓库下载 一 小程序是什么 xff1f 学习小程序之前 xff0c 先简单说一下 xff0c 它到底是什么 字面上讲 xff0c 小程序就是微信里面的应用程序 xff0c 外部代
  • rtp载荷H264解包过程分析,ffmpeg解码qt展示

    网络抽象层单元 NALU NALU头 NALU 头 由1个byte组成 它的语法如下 43 43 0 1 2 3 4 5 6 7 43 43 43 43 43 43 43 43 43 F NRI Type 43 43 F 1 个比特 for
  • CPU上下文切换、进程上下文、中断上下文

    由于Linux是一个多任务操作系统 xff0c 能够支持远大于CPU数量的任务同时运行 当然 xff0c 这些任务实际上并不是真的在同时运行 xff0c 而是由CPU进行调度 xff0c 将时间分片 xff0c 每个任务占用1个时间片 xf
  • Gstreamer概述

    1 什么是GStreamer GStreamer 是用来构建流媒体应用的开源多媒体框架 framework xff0c 其基本设计思想来自于俄勒冈 Oregon 研究生学院有关视频管道的创意 同时也借鉴了DirectShow的设计思想 其目
  • grbl学习之旅---serial篇

    serial c和serial h文件是实现了通过串行端口发送和接受字节的功能 首先是serial h中定义了基本函数和常量大小 xff1a ifndef RX BUFFER SIZE define RX BUFFER SIZE 128 定
  • ip rule,ip route,iptables 三者之间的关系

    以一例子来说明 xff1a 公司内网要求192 168 0 100 以内的使用 10 0 0 1 网关上网 xff08 电信 xff09 xff0c 其他IP使用 20 0 0 1 xff08 网通 xff09 上网 首先要在网关服务器上添
  • 什么是Zero-copy零拷贝

    考虑这样 种常 的情形 xff1a 你需要将静态内容 xff08 类似图 件 xff09 展 给 户 那么这个情形就意味着你需要先将静态内容从磁盘中拷贝出来放到 个内存buf中 xff0c 然后将这个buf通过socket传输给 户 xff

随机推荐

  • 图解协程原理

    前言 协程 Coroutines xff0c 是 Kotlin 最神奇 的特性 xff0c 没有之一 本文会以图解 43 动画的形式解释 Kotlin 协程的原理 看完本文后 xff0c 你会发现 xff0c 原来协程也没有那么难 本文要求
  • ubuntu 16.04下安裝和配置ros

    書上和網上關於ubuntu下安裝ros的文章很多 xff0c 但是很多介紹的不完整 xff0c 並且ubuntu和ros之間其實是有版本對應關系的 xff0c 並不是所有的ros都能安裝到所有的ubuntu上 xff0c xff08 很多書
  • Ubuntu 16.04安装docker详细步骤

    因需要安装opendronemap 而这个依赖于docker 所以记录了一下安装docker的步骤 比较简单 通过apt的docker官方源安装最新的Docker CE Community Edition xff0c 即Docker社区版
  • 在本地shell脚本中ssh到远程服务器并执行命令

    shell远程执行 xff1a 经常需要远程到其他节点上执行一些shell命令 xff0c 如果分别ssh到每台主机上再去执行很麻烦 xff0c 因此能有个集中管理的方式就好了 一下介绍两种shell命令远程执行的方法 前提条件 xff1a
  • Catkin_make执行过程

    这是一个比较复杂的问题 xff0c 但是有时候会有莫名其妙的编译错误 xff0c 在找错误的过程中会非常需要了解这个过程 1 模板文件 首先说一下 in文件 在catkin的目录中有许多 in文件 这些都是模板文件 xff0c 以 opt
  • Docker用yum安装步骤

    Docker用yum安装步骤 一 安装docker xff08 完整版 xff09 1 Docker 要求 CentOS 系统的内核版本高于 3 10 uname r 2 使用 root 权限登录 Centos 确保 yum 包更新到最新
  • 1024,如果全世界程序员都消失了,会怎样?

    这两天 xff0c 有一个话题引起了程序员的广泛讨论 xff1a 年薪80W程序员相亲被鄙视 某知名互联网社区 xff0c 一网友发帖 xff0c 自己年薪80W去相亲 xff0c 竟然被鄙视不如在二本学校教书的大学老师 估计令他没想到的是
  • 非线性控制1.1——稳定与跟踪问题概念

    一 非线性控制系统的两大任务 1 稳定 xff08 或称调节 xff09 问题 稳定问题是要使得闭环系统的状态稳定在一个平衡点附近 对于稳定问题 xff0c 系统的输出不一定要有具体的物理意义 xff0c 此时可以借助输入 输出状态线性化方
  • 在 linux ubuntu 18.04 上运行QQ音乐

    在 linux ubuntu 18 04 上运行QQ音乐 我使用的组合为 ubuntu 18 04 43 wine stable 3 6 43 QQ音乐17 63 xff0c 未在其它平台做过尝试 一直想在ubuntu上好好听音乐 xff0
  • 非线性控制1.0——自适应控制和鲁棒控制

    1 鲁棒控制和自适应控制的联系与区别 鲁棒控制是以目的定义的控制方法集合 xff0c 自适应控制是以手段定义的控制方法集合 xff0c 这两种控制都是为了应对 当数学模型不能精确表示实际系统的情况下 狭义的鲁棒控制是指H2 xff0c Hi
  • 非线性控制2.0——鲁棒控制之H无穷控制器设计

    一 基本概念 对于图1所示系统 xff0c u为控制输入 xff0c y为测量输出 xff0c z为被调输出 xff0c w为干扰输入 xff0c 由输入u xff0c w到输出y xff0c z的传递函数G成为增广被控对象 xff0c 控
  • 非线性控制1.0——控制理论生态及结构

    一 控制理论地图 二 控制理论发展及结构 上图应用于 xff1a https www zhihu com people xiang yi 55 49 answers
  • 四旋翼飞行器——飞行原理

    1 机械结构 旋翼对称分布在机体的前后 左右四个方向 xff0c 四个旋翼处于同一高度平面 xff0c 且四个旋翼的结构和半径都相同 xff0c 四个电机对称的安装在飞行器的支架端 xff0c 支架中间空间安放飞控板 结构形式如图 1 1所
  • 四旋翼飞行器——非线性化模型

    一 建模思路 该模型目标控制量是机体相对于地面坐标系的线速度的三个分量Vx xff0c Vy xff0c Vz xff0c 而我们能控的实质上只有四个电机的转速W1 xff0c W2 xff0c W3 xff0c W4 怎样由输入一步步推导
  • 故障诊断4—龙伯格状态观测器设计

    1 龙伯格状态观测器概念 已经线性系统模型如下 xff1a 当系统状态量难以获取 xff0c 但实际控制中又需要利用系统状态量时 xff0c 如何通过输入量和输出量重构系统状态量 xff1f 这便是龙伯格状态观测器设计初衷 xff0c 将实
  • 故障诊断5——状态观测器和输出观测器

    1 状态观测器分类 在现代控制理论中 xff0c 控制系统的基本结构和经典控制理论一样 xff0c 仍然是由受控对象和反馈控制器两部分构成的闭环系统 不过在经典理论中习惯于采用输出反馈 xff0c 而在现代控制理论中则更多地采用状态反馈 由
  • GPS漂移和定位不准确的解决办法

    解决GPS漂移主要从两方面入手 xff1a 一 主系统的设计主要减少在近距离内对GPS信号的干扰 二 软件处理 软件处理主要集中在导航软件处完成 xff0c 导航软件会将坐标定位在道路之内 xff0c 如果GPS接收到的信号超出道路的半径范
  • AI---是什么?可以做什么?

    1 AI的项目简单介绍 图像识别 描述 xff1a 给定图片 xff0c 识别图片中有什么 xff1f 算法 xff1a KNN CNN 情感分析 描述 xff1a 判断文本包含的情感是正面 负面还是中性 关键 xff1a 文本如何表示成向
  • realsense的安装问题

    realsense的安装问题 0 旁白1 SDK的安装2 python开发包的安装3 nodejs开发包的安装方法1 xff1a 方法2 xff1a 接手一位同事的realsense相关项目 xff0c 先配置一个环境 xff0c 出现不少
  • 二叉排序树的删除

    xff08 网上讲二叉排序树删除的资料很少 xff0c 这篇很不错 xff01 转自 xff1a http bbs csdn net topics 110010437 xff09 二叉排序树的删除 xff1a 对于一般的二叉树来说 xff0