查找-之二叉排序树(查找、插入、删除)

简介: 有序的线性表采用:折半/二分、插值、斐波那契查找相比顺序查找效率得到提高,但是在插入和删除时效率低(为维持数据的有序性) 在高效实现查找操作时,如何提高插入和删除的效率?在一些应用场景:在查找时需要插入和删除

问题:

有序的线性表采用:折半/二分、插值、斐波那契查找相比顺序查找效率得到提高,但是在插入和删除时效率低(为维持数据的有序性)

在高效实现查找操作时,如何提高插入和删除的效率?

在一些应用场景:在查找时需要插入和删除

解决方法:

二叉排序树


二叉排序树特点

1)若左子树不为空,左子树上所有结点的值均小于或等于它的根节点的值

2)若右子树不为空,右子树上所有结点的值均大于或等于它的根节点的值

3)左右子树也分别为二叉排序树

二叉排序算法的复杂度:

时间复杂度:二分查找的思想,查找次数为二叉查找树的高度,若树为平衡二叉树则为O(logn),否则最坏的情况为右斜树O(n)

二叉排序算法的缺点是:

二叉树的构建类型多种,不同的二叉树形状会导致查找的性能差异很大,例如普通的二叉树和一棵右斜树。

二叉排序树的查找:

1)查找数据key,判断key是否等于树的根节点数据

2)若待查数据key小于根结点数据则递归的在左子树查找

3)若待查数据key大于根结点数据则递归的在右子树查找

C伪代码:

//定义结点结构
typedef struct BiTNode
{
   int data;  //结点数据
   struct BiTNode *lchild, *rchild;//左右孩子指针
}BiTNode,*BiTree


/递归查找二叉排序树T中是否存在key
//f指向T的双亲 
//p获得查找到的结点位置
Status SearchBST(BiTree T,int key,BiTree f,BiTree *p)
{
   //查找不成功
    if(!T)//判断当前二叉树是否到叶子结点
     {
        *p=f;//指针p指向查找路径上访问的最后一个结点并返回false
        return False;
     }
   //查找成功
   else if(key==T->data)
    {
       *p=T;
      return True;
   }
  else if(key<T->data)//待查找元素小于结点数据
      return SearchBST(T->lchild,key,T,p);//在左子树继续查找
  else
      return SearchBST(T->rchild,key,T,p);//在右子树继续查找
}

二叉排序树的插入操作:

1)在二叉排序树找不到待插入的数据key则执行2)步骤

2)待插数据初始化为结点s,若树为空则直接赋值结点s给树

3)待插入数据key小于根结点数据则插入为左孩子

4)  待插入数据key大于根结点数据则插入为右孩子

C伪代码:

//定义结点结构
typedef struct BiTNode
{
   int data;  //结点数据
   struct BiTNode *lchild, *rchild;//左右孩子指针
}BiTNode,*BiTree
//二叉树的数据插入
Status InsertBST(BiTree *T,int key)
{
  BiTree p,s;//创建二叉树结点
  //在二叉排序树中找不到key
  if(!SearchBST(*T,key,NULL,&p))
    {
        //s结点的初始化
        s=(BiTree)malloc(sizeof(BiTNode));
        s->data=key;
        s->lchild=s->rchild=NULL;
        //若p结点为空
        if(!p)
              *T=s;
        else if (key<p->data)//待插入的值key小于p结点指向的数据
              p->lchild=s;//s插入为左孩子
        else//待插入的值key大于p结点指向的数据
             p->rchild=s;//s插入为右孩子
       return True;
   }
//树中已有关键字相同的结点,不再插入
  else
     return False;
}

构建一棵二叉排序树示例:

//生成一棵二叉树
int i;
int a[10]={62,88,58,47,35,73,51,99,37,93};
Bitree T=NULL;
for(i=0;i<10;i++)
{
    InsertBST(&T,a[i]);
}

二叉排序树的删除操作:

删除结点的三种情况:

1)删除叶子结点

2)删除的结点只有左或右子树的

3)删除的结点有左右子树

//定义结点结构
typedef struct BiTNode
{
   int data;  //结点数据
   struct BiTNode *lchild, *rchild;//左右孩子指针
}BiTNode,*BiTree
//删除元素等于key的数据结点
Status DeleteBST(BiTree *T,int key)
{
 //不存在关键字等于key的数据元素
   if(!*T)
       return False;
  else
  {
       if(key==(*T)->data)     //找到关键字等于key的数据元素
               return Delete(T);
       else if(key<(*T)->data)//待删除的元素key小于查找到的元素---则在结点的左子树搜索
              return DeleteBST(&(*T)->lchild,key);
       else                               //待删除的元素key大于查找到的元素---则在结点的右子树搜索
              return DeleteBST(&(*T)->rchild,key);
  }
}
Status Delete(BiTree *p)
{ 
     BiTree q,s;
    //第一种情况,删除结点只有左子树或右子树
     if((*p)->rchild==NULL)//只有左子树
      {  
            q=*p;
            *p=(*p)->lchild; 
            free(q);
      }  
    else if((*p)->lchild==NULL)//只有右子树
     {
          q=*p;
          *p=(*p)->rchild;
          free(q);
    }
  //第二种情况:删除的结点有左子树和右子树
  else
    {
      q=*p;//待删除的结点给临时变量q
      s=(*p)->lchild;//待删除结点指向的左子树给临时变量s
      while(s->rchild)//左子树s一直向右找,直到找到待删结点的前驱
         {
            q=s;
            s=s->rchild;
         }
    (*p)->data=s->data;//s指向被删结点的直接前驱,将它的值直接赋值给要删除的结点*p
    if(q!=*p)//被删结点的直接前驱p!=被删结点的直接前驱的根结点q
       q->rchild=s->lchild;//根结点q的右孩子指针指向被删结点的直接前驱的左孩子
    else
        q->lchild=s->lchild;
   free(s);
    } 
}

上述代码需注意:

1)q!=*p的情况:

0fa82f70d8a010f4fbf68a49eec467dc_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2hlZGEz,size_16,color_FFFFFF,t_70.png

db83bb2d07500a9a2528e69e3f9f0424_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2hlZGEz,size_16,color_FFFFFF,t_70.png

2) q=*p的情况:

若上图的结构修改为:

没有结点37和36,s指向35。

p和q的指向相同都是47结点处,则将s->lchild指向的29赋值给q->lchild

 

后续:如何解决二叉排序树多次插入新结点而导致的不平衡?

目录
相关文章
|
SQL 存储 NoSQL
SQL vs. NoSQL:如何根据大数据需求选择合适数据库
【4月更文挑战第8天】本文对比分析了SQL与NoSQL数据库在大数据项目中的应用。SQL数据库适合结构化数据、强一致性和复杂事务处理,如金融系统,而NoSQL则适用于半结构化和非结构化数据、高并发及大数据场景,如社交网络。选择时应考虑业务需求、技术栈、团队经验和成本效益,以找到最佳解决方案。随着技术发展,NewSQL和Multi-model数据库也提供了更多选择。
748 0
|
机器学习/深度学习 数据建模 定位技术
【数据结构】图的基本概念—无/有向图、权和网、完全图、路径与回路
【数据结构】图的基本概念—无/有向图、权和网、完全图、路径与回路
5025 0
【数据结构】图的基本概念—无/有向图、权和网、完全图、路径与回路
|
3月前
|
人工智能 缓存 自然语言处理
别再手搓测试数据了!AE测试数据智造系统揭秘
本文介绍如何通过构建基于大语言模型的测试数据智造Agent,解决AliExpress跨境电商测试中数据构造复杂、低效的问题,推动测试效率提升与智能化转型。
别再手搓测试数据了!AE测试数据智造系统揭秘
|
10月前
|
数据采集 数据挖掘 数据格式
使用Python进行数据清洗的实用指南
在数据分析的世界里,"垃圾进,垃圾出"这句老话再贴切不过。数据清洗作为数据分析前的关键步骤,直接影响着分析结果的准确性与可靠性。本文将通过浅显易懂的语言和实际代码示例,带你掌握如何使用Python及其强大的库进行数据清洗,从缺失值处理到异常值检测,再到数据格式转换和重复数据删除,让你的数据准备工作变得既高效又专业。
563 2
|
存储 算法 C语言
C语言手撕实战代码_二叉排序树(二叉搜索树)_构建_删除_插入操作详解
这份二叉排序树习题集涵盖了二叉搜索树(BST)的基本操作,包括构建、查找、删除等核心功能。通过多个具体示例,如构建BST、查找节点所在层数、删除特定节点及查找小于某个关键字的所有节点等,帮助读者深入理解二叉排序树的工作原理与应用技巧。此外,还介绍了如何将一棵二叉树分解为两棵满足特定条件的BST,以及删除所有关键字小于指定值的节点等高级操作。每个题目均配有详细解释与代码实现,便于学习与实践。
296 3
|
10月前
|
机器学习/深度学习 存储 算法
数据结构实验之二叉树实验基础
本实验旨在掌握二叉树的基本特性和遍历算法,包括先序、中序、后序的递归与非递归遍历方法。通过编程实践,加深对二叉树结构的理解,学习如何计算二叉树的深度、叶子节点数等属性。实验内容涉及创建二叉树、实现各种遍历算法及求解特定节点数量。
249 4
|
11月前
|
网络安全 数据安全/隐私保护 C++
VS Code 的SSH连接不成功问题分析与解决
VS Code 的SSH连接不成功问题分析与解决
|
网络协议 Unix Linux
docker部署Portainer
Portainer可以在Docker上运行,而且部署起来非常简单 Portainer是Docker的图形化管理工具,提供状态显示面板、应用模板快速部署、容器镜像网络数据卷的基本操作(包括上传下载镜像,创建容器等操作)、事件日志显示、容器控制台操作、Swarm集群和服务等集中管理和操作、登录用户管理和控制等功能。功能十分全面,基本能满足中小型单位对容器管理的全部需求
docker部署Portainer
|
11月前
|
编解码 vr&ar
GANs在图像生成领域有哪些应用呢
【10月更文挑战第14天】GANs在图像生成领域有哪些应用呢
281 0
|
算法
路径规划算法 - 求解最短路径 - Dijkstra(迪杰斯特拉)算法
路径规划算法 - 求解最短路径 - Dijkstra(迪杰斯特拉)算法
476 0