数据结构:二叉排序树

news2025/6/23 20:20:28

什么是二叉排序树?

二叉排序树要么是空二叉树,要么具有如下特点:

  • 二叉排序树中,如果其根结点有左子树,那么左子树上所有结点的值都小于根结点的值;
  • 二叉排序树中,如果其根结点有右子树,那么右子树上所有结点的值都大小根结点的值;
  • 二叉排序树的左右子树也要求都是二叉排序树;

如图所示,就是一个二叉排序树:

在这里插入图片描述

使用二叉排序树查找关键字

二叉排序树中查找某关键字时,查找过程类似于次优二叉树,在二叉排序树不为空树的前提下,首先将被查找值同树的根结点进行比较,会有3种不同的结果:

  • 如果相等,查找成功;
  • 如果比较结果为根结点的关键字值较大,则说明该关键字可能存在其左子树中;
  • 如果比较结果为根结点的关键字值较小,则说明该关键字可能存在其右子树中;

实现函数为:(运用递归的方法)

BSTree SreachBST(BSTree T, int key){
		//如果递归过程中 T 为空,则查找结果,返回NULL;或者查找成功,返回指向该关键字的指针
    if ((!T) || (key == T->data)) return T;
    else if(key < T->data) return SreachBST(T->lchild, key);//递归遍历其左孩子
    else return SreachBST(T->rchild, key);//递归遍历其右孩子
}

二叉排序树中插入关键字

二叉排序树本身是动态查找表的一种表示形式,有时会在查找过程中插入或者删除表中元素,当因为查找失败而需要插入数据元素时,该数据元素的插入位置一定位于二叉排序树的叶子结点,并且一定是查找失败时访问的最后一个结点的左孩子或者右孩子。

例如,在图 1 的二叉排序树中做查找关键字 1 的操作,当查找到关键字 3 所在的叶子结点时,判断出表中没有该关键字,此时关键字 1 的插入位置为关键字 3 的左孩子。

所以,二叉排序树表示动态查找表做插入操作,只需要稍微更改一下上面的代码就可以实现,具体实现代码为:

void InserBST(BSTree &T, int key){
    if(!T){//如果递归过程中T为空,则初始化插入结点
        BSTNode *S = new BSTNode;
        S->data = key;
        S->lchild = NULL;
        S->rchild = NULL;
        T = S;
    }
    else if(key < T->data) InserBST(T->lchild, key);
    else if(key > T->data) InserBST(T->rchild, key);
    else if(key == T->data) {cout << "该关键字在已存在!!!";return;}
}

通过使用二叉排序树对动态查找表做查找和插入的操作,同时在中序遍历二叉排序树时,可以得到有关所有关键字的一个有序的序列。

例如,假设原二叉排序树为空树,在对动态查找表 {3,5,7,2,1} 做查找以及插入操作时,可以构建出一个含有表中所有关键字的二叉排序树,过程如图 2 所示:

在这里插入图片描述

图 2 二叉排序树插入过程

通过不断的查找和插入操作,最终构建的二叉排序树如图 2(5) 所示。当使用中序遍历算法遍历二叉排序树时,得到的序列为: 1 2 3 5 7 ,为有序序列。

一个无序序列可以通过构建一棵二叉排序树,从而变成一个有序序列。

二叉排序树中删除关键字

在查找过程中,如果在使用二叉排序树表示的动态查找表中删除某个数据元素时,需要在成功删除该结点的同时,依旧使这棵树为二叉排序树。

假设要删除的为结点 p ,则对于二叉排序树来说,需要根据结点 p 所在不同的位置作不同的操作,有以下 3 种可能:

1、结点 p 为叶子结点,此时只需要删除该结点,并修改其双亲结点的指针即可;

2、结点 p 只有左子树或者只有右子树,如果 p 是其双亲节点的左孩子,则直接将 p 节点的左子树或右子树作为其双亲节点的左子树;反之也是如此,如果 p 是其双亲节点的右孩子,则直接将 p 节点的左子树或右子树作为其双亲节点的右子树;

3、结点 p 左右子树都有,此时有两种处理方式:

1)令结点 p 的左子树为其双亲结点的左子树;结点 p 的右子树为其自身直接前驱结点的右子树,简单理解:因为p的左子树所有结点都小于p的右子树结点,所以只需将p的左子树重接在p的父结点的左子树上,然后将p的右子树整体接入p左子树的最右端s即可。如图 3 所示;

在这里插入图片描述

图 3 二叉排序树中删除结点(1)

2)用结点 p 的直接前驱(或直接后继)来代替结点 p ,同时在二叉排序树中对其直接前驱(或直接后继)做删除操作,简单理解:因为p的左子树的最右端s是p的左子树所有结点关键字中最大的,所以只需用其代替p,然后将s的左子树为其父结点的右子树即可。如图 4 为使用直接前驱代替结点 p :

在这里插入图片描述

图 4 二叉排序树中删除结点(2)

图 4 中,在对左图进行中序遍历时,得到的结点 p 的直接前驱结点为结点 s,所以直接用结点 s 覆盖结点 p,由于结点 s 还有左孩子,根据第 2 条规则,直接将其变为双亲结点的右孩子。

具体实现代码:(可运行)

typedef struct BSTNode{
    int data;
    struct BSTNode *lchild, *rchild;
}BSTNode, *BSTree;

BSTree SreachBST(BSTree T, int key){
		//如果递归过程中 T 为空,则查找结果,返回NULL;或者查找成功,返回指向该关键字的指针
    if ((!T) || (key == T->data)) return T;
    else if(key < T->data) return SreachBST(T->lchild, key);//递归遍历其左孩子
    else return SreachBST(T->rchild, key);//递归遍历其右孩子
}
void InserBST(BSTree &T, int key){
    if(!T){//如果递归过程中T为空,则初始化插入结点
        BSTNode *S = new BSTNode;
        S->data = key;
        S->lchild = NULL;
        S->rchild = NULL;
        T = S;
    }
    else if(key < T->data) InserBST(T->lchild, key);
    else if(key > T->data) InserBST(T->rchild, key);
    else if(key == T->data) {cout << "该关键字在已存在!!!";return;}
}
void CreatBST(BSTree &T){
    T = NULL;
    int key;
    cin >> key;
    while(key != -1){//-1表示输入结束
        InserBST(T, key);
        cin >> key;
    }
}
void DeleteBST(BSTree &T, int key){
    BSTree p = T, f = NULL;
    while (p){//找到要删除的关键字
        if (p->data == key) break;
        f = p;//f为p的父节点
        if (p->data > key) p = p->lchild;
        else p = p->rchild;
    }
    if (!p) return; //若p为空,则表示未找到要删除的结点
    BSTree q = p;
    if ((p->lchild) && (p->rchild)){//被删除的结点左右子树均不为空
        BSTree s = p->lchild;
        while (s->rchild){//在p的左子树中找到其前驱结点,即最右下结点
            q = s;
            s = s->rchild;
        }
        p->data = s->data;
        if(q != p) q->rchild = s->lchild;//接入q的右子树
        else q->lchild = s->lchild;//接入q的左子树
        delete s;
        return;
    }
    else if (!p->rchild){//被删除结点p没有右子树,只需重接其左子树
        p = p->lchild;
    }
    else if (!p->lchild){//被删除结点p灭有左子树,只需重接其右子树
        p = p->rchild;
    }
    if (!f) T = p;//被删除的是更节电
    else if (q = f->lchild) f->lchild = p;//q在那边就将p重接到那边
    else f->rchild = p;
    delete q;
}

总结

使用二叉排序树在查找表中做查找操作的时间复杂度同建立的二叉树本身的结构有关。即使查找表中各数据元素完全相同,但是不同的排列顺序,构建出的二叉排序树大不相同。

例如:查找表 {45,24,53,12,37,93} 和表 {12,24,37,45,53,93} 各自构建的二叉排序树图下图所示:

在这里插入图片描述

图 5 不同构造的二叉排序树

使用二叉排序树实现动态查找操作的过程,实际上就是从二叉排序树的根结点到查找元素结点的过程,所以时间复杂度同被查找元素所在的树的深度(层次数)有关。

为了弥补二叉排序树构造时产生如图 5 右侧所示的影响算法效率的因素,需要对二叉排序树做“平衡化”处理,使其成为一棵平衡二叉树。 平衡二叉树是动态查找表的另一种实现方式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1086218.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Nlopt在matlab中的配置教程

step1&#xff1a;克隆代码并编译 编译的前提是已经安装好MinGW64 # 使用镜像加速 git clone https://gitclone.com/github.com/stevengj/nloptcd nlopt mkdir build cd build cmake -G"MinGW Makefiles" .. cmake --build .# 注意此处博主在mingw安装目录将mingw3…

golang 獲取 prometheus數據

使用github上的一個庫 1.安裝庫 go get github.com/prometheus/client_golang 2.導入 在import中導入&#xff0c;記得要在go.mod中更新一下 ------------------------------------------------------------------------------------ Address: "http://xx.xx.xx:9090…

ROS IMU 数据发布---rviz_imu_plugin的安装

ROS中发布IMU传感器消息 - 润新知 按照上述链接的方法执行 catkin_make install -DCMAKE_INSTALL_PREFIX/opt/ros/noetic 后报错 这个错误是因为在安装过程中&#xff0c;CMake无法将文件复制到目标路径。这可能是由于权限不足导致的。可以尝试使用以下命令更改目标文件夹的…

Mac删除不在程序坞的程序

现象描述&#xff1a;删除某个程序时&#xff08;通过‘程序’列表中将该应用移动到废纸篓里&#xff09;&#xff0c;该应用程序正在运行中&#xff0c;删除过程该程序未提示正在运行中&#xff0c;仅仅删除了图标&#xff08;在此吐槽下该程序的交互&#xff0c;产品没有考虑…

sql文件数据量太大,不打开sql文件,使用sqlplus往oracle数据库中写入大量数据

1、sqlplus 用户名/密码XE 2、往数据库中写入语句的sql文件路径 3、commit; (分号要写)如果不提交的话&#xff0c;数据库中新写入的数据不显示。

Allegro在测量时如何同时显示双单位

Allegro在测量时,默认只显示单个单位,根据PCB文件设计时所设定的单位决定,显示mil或mm。对于习惯看某个参数的设计者来说不方便。那如何同时显示mil和mm双单位呢? 点击Setup菜单 选择User Preferences...(用户参数设置) 跳出下面的对话框,选择Display→Element(元素)→…

【error】root - Exception during pool initialization

报错提示&#xff1a;root - Exception during pool initialization. 错误原因&#xff1a; 配置数据库出错 我的错误配置&#xff1a; spring.datasource.urljdbc:mysql://localhost:3306/springboot?serverTimezoneGMT spring.datasource.nameroot spring.datasource.pass…

Java实现基数排序

今天迷茫一天&#xff0c;没时间补博客&#xff0c;我就分享一个我认为最好的排序&#xff0c;思想也很容易解决很多关于字符串和数组问题&#xff0c;举个例子吧&#xff0c;求相邻元素的差值。 1.前言 基数排序是一种非比较排序算法&#xff0c;它将待排序的数字按照其每一位…

stm32学习笔记:EXIT中断

1、中断系统 中断系统是管理和执行中断的逻辑结构&#xff0c;外部中断是众多能产生中断的外设之一。 1.中断&#xff1a; 在主程序运行过程中&#xff0c;出现了特定的中断触发条件 (中断源&#xff0c;如对于外部中断来说可以是引脚发生了电平跳变&#xff0c;对于定时器来…

el-table组件的封装

前言&#xff1a;仔细看懂本篇博客&#xff0c;玩转element table 不成问题 &#xff0c;个人理解所谓封装&#xff0c;就是把经常都要公用的东西&#xff0c;拿出来&#xff0c;可以多次复用。公用方法&#xff0c;公用页面都可以封装。 其实封装也并不是有多难&#xff0c;思…

Linux和UNIX的关系及区别

UNIX 与 Linux 之间的关系是一个很有意思的话题。在目前主流的服务器端操作系统中&#xff0c;UNIX 诞生于 20 世纪 60 年代末&#xff0c;Windows 诞生于 20 世纪 80 年代中期&#xff0c;Linux 诞生于 20 世纪 90 年代初&#xff0c;可以说 UNIX 是操作系统中的"老大哥&…

瑞芯微 | 如何固定以太口地址为指定ip?

rxw的RK3568的evb1公板&#xff0c;有2个以太口&#xff0c; 默认UI界面只能配置eth0&#xff0c;无法配置eth1&#xff0c; 实际应用中&#xff0c;有时需要一旦有网线插入&#xff0c;就需要该地址设置为指定IP地址。 本文介绍2个最简单的方法实现固定IP。 一、通过修改i…

电脑提示kernel32.dll的错误提示窗口怎么办,解决kernel32.dll丢的办法

当你在使用电脑时&#xff0c;突然收到kernel32.dll丢失或找不到的错误提示窗口&#xff0c;这个时候先不要让自己的心态爆炸&#xff0c;解决的办法会有很多种&#xff0c;其实问题都不大&#xff0c;就能够很好的解决文件缺失的问题。接下来就把方法推进给大家。 一.解决kern…

Docker逃逸---CVE-2020-15257浅析

一、产生原因 在版本1.3.9之前和1.4.0~1.4.2的Containerd中&#xff0c;由于在网络模式为host的情况下&#xff0c;容器与宿主机共享一套Network namespace &#xff0c;此时containerd-shim API暴露给了用户&#xff0c;而且访问控制仅仅验证了连接进程的有效UID为0&#xff…

网站列表页加密:三次请求后返回内容多\r

一、抓包第一次请求 url aHR0cDovL2N5eHcuY24vQ29sdW1uLmFzcHg/Y29saWQ9MTA抓包&#xff0c;需要清理浏览器cookie&#xff0c;或者无痕模式打开网址&#xff0c;否则返回的包不全&#xff0c;依照下图中的第一个包进行requests请求 第一次请求后返回 <!DOCTYPE html>…

每年高考时间是几月几号 高考开始时间

高考是高中生最重要的一个阶段&#xff0c;甚至影响着很多学生的未来&#xff0c;相信大家都很关注高考的具体时间是什么时候&#xff0c;本次将详细给您介绍高考的具体开始时间以及结束时间。 每年高考的时间都是6月7日开始&#xff0c;一共持续三天时间左右&#xff0c;但是…

Java面试题-0919

集合篇 Java面试题-集合篇HashMap底层实现原理概述javaSE进阶-哈希表 为了满足hashmap集合的不重复存储&#xff0c;为什么要重写hashcode和equals方法&#xff1f; 首先理解一下hashmap的插入元素的前提&#xff1a; hashmap会根据元素的hashcode取模进行比较&#xff0c;当…

HDLbits: Lemmings3

Lemmings又多了一种状态&#xff1a;dig&#xff0c;我按照上一篇文章里大神的思路又多加了两种状态&#xff1a;LEFT_DIGGING与RIGHT_DIGGING&#xff0c;写出了如下的代码&#xff1a; module top_module(input clk,input areset, // Freshly brainwashed Lemmings walk …

css中filter属性设置后导致页面定位失效

问题&#xff1a;app上设置css的filter属性导致定位失效。 原因&#xff1a;当在标签中使用了 filter 属性后&#xff08;body { filter: grayscale(1); &#xff09;&#xff0c; filter 就会生成一个新的包含块&#xff0c;其位置大小和所在标签一样&#xff0c;然后 fixed …

Java BIO模型分析(提供单线程和多线程服务端代码示例)

目录 一、BIO特点介绍二、BIO代码实现2.1、客户端代码准备2.2、服务端单线程处理2.2.1、服务端代码2.2.2、阻塞代码分析2.2.3、存在问题 2.3、服务端多线程处理2.3.1、服务端代码2.3.2、存在问题 一、BIO特点介绍 BIO(blocking I/O)&#xff1a;同步阻塞IO&#xff0c;在每个I…