(原创)像极了爱情的详解排序二叉树,一秒get

排序二叉树(建立、查找、删除)

二叉树我们已经非常熟悉了,但是除了寻常的储存数据、遍历结构,我们还能用二叉树做什么呢?

我们都知道不同的遍历方式会对相同的树中产生不同的序列结果,排序二叉树就是利用二叉树的遍历特征实现的特殊树种,也叫二叉查找树。

  1. 排序二叉树从根结点起的每一个结点的左子树元素均小于其自身,右子树元素值均大于其自身
  2. 即任何结点的值均大于其左子树所有元素,均小于其右子树所有元素

如:就是一个排序二叉树,直观的一批,从子树到根结点,永远符合左小右大的规则(中序遍历)

Ⅰ、结构定义

排序二叉树的定义与一般二叉树无异

typedef struct BiTree{
    int item;
    struct BiTree *lchild,*rchild;
}BiTree;

Ⅱ、排序二叉树的查找

我们先来看一下排序二叉树的查找实现,因为插入在排序二叉树中是实现后续建立、删除结点的基础,因为结点带有顺序,故而遍历条件有所改变,代码如下:

int searchnode(BiTree *t, int key)
{
    if(!t)
        return 0;
    if(t->item == key)
    {
        return 1;
    }
    else
    {
        if(t->item > key)
            return searchnode(t->lchild,key);
        if(t->item < key)
            return searchnode(t->rchild,key);
    }
}

清爽递归,不解释

Ⅲ、二叉排序树的插入

void insertbitree(BiTree **t, int value)
{
    if(!searchnode(*t,value))
    {
        if(*t == NULL)
        {
            *t = (BiTree *)malloc(sizeof(BiTree));
            (*t)->item = value;
            (*t)->lchild = NULL;
            (*t)->rchild = NULL;
        }
        else
        {
            if((*t)->item > value)
                insertbitree(&((*t)->lchild), value);
            else
                insertbitree(&((*t)->rchild), value);
        }
    }
}

这个插入上来先判断一哈我们现有的树里面有没有这个元素,如果有就不会进入循环,至于插入操作的框架也基本符合中序遍历的操作,只是加上了判断大小

Ⅳ、二叉排序树删除结点(HARD)

轻松愉快的建立、查找排序二叉树的操作完成之后,我们来看看比较困难的删除排序二叉树结点的操作。为什么说它困难呢,相比插入或者查找,删除面对的是一个已经成型的树,我们不仅要考虑怎样去掉这个结点,还要想到按照中序以及数字大小将原有结点按序放到正确位置。

好的,我们先来考虑一下我们可能删除哪几种结点:

第一类:待删除结点只有左子树,没有右子树,可以想见,这种情况下只需要把后续的左子树接到待删除结点的上一个结点上,再释放待删除结点的空间就OK

第二类:带删除结点只有右子树,没有左子树,跟第一类一个道理,这样的操作只需要三行就解决,但是棘手的问题总在短暂的轻松过后

第三类:这一类情况就是大魔王辽,左右孩子一个不缺,手心手背都是肉,哪个也不能少,怎么解决这个问题呢?让我们来看一个例子。

看这个丑不拉叽的排序二叉树,非常体现中序遍历特点

现在我们要删除 34 这个结点,就是我们刚才说的那种第三类情况,左右均有结点,这个时候,我们有这两种方法阔以达成目的

第一种:姑且叫他 牺牲前驱法 ,我们要去掉 34 ,就要把他的前驱拿来顶替这个位置,保持二叉排序树的序,然后当然要检测一下,如果牺牲的这个前驱点(在我们这里是 33 )有子树,还需要把子树和上一级连上(如32),这是第一种方法

  1. 用直接前驱 33 替换 34 
  2. 删除原有的 33 结点
  3. 把结点 32 ,移到原 33 位置

第二种:相信你也猜到了,牺牲后继法,反正兄弟两个要挑一个顶上去,让我们看一哈在这个例子中,怎么个牺牲后继

35 已经被我们放上来辽

  1. 用直接后继 35 替换 34
  2. 删除结点 35 

因为这里的 35 茕茕孑立,没儿没女,所以这个例子的这里不需要连接子树,但是千万注意不要认为所有的替换后继法都不用管子树

好的,方法讲明白了辽,我们代码实现一哈

int Delete(BiTree **t)
{
    BiTree *s,*q;
    if((*t) -> lchild == NULL)//左子树空的情况
    {
        q = *t;
        *t = (*t)->lchild;
        free(q);
    }
    else if( (*t) -> rchild == NULL)//右子树为空的情况
    {
        q = *t;
        *t = (*t)->rchild;
        free(q);
    }
    else //左右子数均为空
    {
        q = *t;
        s = (*t)->lchild;
        while(s->rchild)//循环找到直接前驱
        {
            q = s;
            s = s->rchild;
        }
        (*t) -> item = s -> item; //结点数据替换
        if(q != *t) //接原有左右子树
            q->rchild = s->lchild;
        else
            q->lchild = s->lchild;
        free(s);
    }
    return 1;
}
int DeleteBST(BiTree **T, int key)
{

    if (!*T)
        return 0;
    else
    {
        if (key ==  (*T)->item)
            return Delete(T);
        else if (key < (*T)->item)
            return DeleteBST(&(*T)->lchild, key);
        else
            return DeleteBST(&(*T)->rchild, key);
    }
    return 1;
}

解读见注释

测试用主函数部分:

int main()
{
    int i;
    BiTree *t = NULL;
    int value[] = {12,24,88,3,64,99,71,64,10,8};
    for(i = 0; i < 10; i++)
        insertbitree(&t,value[i]);
    printf("建立序列为:\n");
    lar(t);
    printf("\n");
    printf("删除结点88,结果为:\n");
    DeleteBST(&t,88);
    lar(t);
    printf("\n");
    return 0;
}

呼~完毕

原文地址:https://www.cnblogs.com/yx1999/p/10352828.html

时间: 2024-10-29 04:16:14

(原创)像极了爱情的详解排序二叉树,一秒get的相关文章

IT段子手详解MyBatis遇到Spring 秒学Java SSM开发大众点评

第1章 课程概览介绍课程目标.开发内容.功能划分.开发顺序,开发所需要的前置知识及环境准备,并且介绍了与课程相关的前后端分离的思想,架构演进过程.1-1 课程导学1-2 功能划分和开发流程1-3 原料准备1-4 武功秘籍 第2章 开发准备演示前端工程环境搭建以及启动步骤,介绍了后台工程初始SSM框架目录结构,将前后端打通,并将后台管理使用的界面原型加入后台工程中,形成一个完整可用的前后台联动的原型.拓展的谈了谈HTTP API和RESTFul API的趣闻.2-1 HTTP API和RESTfu

原创不如山寨——原型模式详解

1. 前言 现实世界中山寨这种行为往往意味着假冒伪劣,备受批判.但是在软件开发中,山寨却又不少可取之处.首先其"成分"和"质量"和原创不相上下:其次相比原创一个东西的时间开销,山寨一个出来总归是省时省力的,毕竟对于计算机,克隆一个对象要比创建一个对象性能好得多(拷贝对象不会执行构造方法).如果在开发中我们需要一个类的多个实例,这些实例只在某些属性细节上不同,相比直接new出它们的时间开销,从一个实例原型拷贝出其他的实例或许是更可取的方法.而原型模式,或者说克隆模式就

(原创)高性能IP数据库格式详解 qqzeng-ip.dat

高性能IP数据库格式详解 qqzeng-ip.dat 编码:UTF8           字节序:Little-Endian 返回多个字段信息(如:亚洲|中国|香港|九龙|油尖旺|新世界电讯|810200|Hong Kong|HK|114.17495|22.327115) ------------------------ 文件结构 --------------------------- //文件头 16字节(4-4-4-4) [索引区第一条流位置][索引区最后一条流位置][前缀区第一条的流位置]

最全C 语言常用算法详解-排序-队列-堆栈-链表-递归-树

具体 源代码 案例查看github,持续更新中............ github地址:https://github.com/Master-fd/C-Algorithm 1. 二分法查找 2. 冒泡排序 3. 插入排序 4. 希尔排序 5. 选择排序 6. 快速排序 7. 单链表实现堆栈 8. 单链表实现队列 9. 普通单链表 10. 递归实现斐波拉契数列 11. 递归实现strlen 12. 循环链表 13. 求素数 14. 双向链表 15. 顺序表实现队列 16. 顺序表实现栈 17. 顺

【原创】ab结果参数详解

解释如下: Server Software 服务器软件软件名称. Server Hostname 被测服务器的主机名. Server Port 被测试的Web服务器的监听端口. SSL/TLS Protocol 仅当使用,才会打印.表示客户端和服务器协商的参数. Document Path 请求URL. Document Length 第一次成功返回的的文档大小,如果文档长度在测试的时候发生变化,这个响应会被当作错误,因此如果失败的请求下面有Length类型的错误,可以考虑是否是因为被测的url

二叉树详解及二叉树的前序、中序、后序遍历(递归和非递归)

介绍二叉树之前先介绍一下树相关的概念. 树的定义:树是n(n>=0)个有限个数据的元素集合,形状像一颗倒过来的树. 树的概念: 节点:结点包含数据和指向其它节点的指针. 根节点:树第一个结点称为根节点. 结点的度:结点拥有的子节点个数. 叶节点:没有子节点的节点(度为0). 父子节点:一个节点father指向另一个节点child,则child为孩子节点,father为父亲节点 兄弟节点:具有相同父节点的节点互为兄弟节点. 节点的祖先:从根节点开始到该节点所经的所有节点都可以称为该节点的祖先. 子

Mysql高手系列 - 第12篇:子查询详解

这是Mysql系列第12篇. 环境:mysql5.7.25,cmd命令中进行演示. 本章节非常重要. 子查询 出现在select语句中的select语句,称为子查询或内查询. 外部的select查询语句,称为主查询或外查询. 子查询分类 按结果集的行列数不同分为4种 标量子查询(结果集只有一行一列) 列子查询(结果集只有一列多行) 行子查询(结果集有一行多列) 表子查询(结果集一般为多行多列) 按子查询出现在主查询中的不同位置分 select后面:仅仅支持标量子查询. from后面:支持表子查询

Mysql高手系列 - 第14篇:详解事务

这是Mysql系列第14篇. 环境:mysql5.7.25,cmd命令中进行演示. 开发过程中,会经常用到数据库事务,所以本章非常重要. 本篇内容 什么是事务,它有什么用? 事务的几个特性 事务常见操作指令详解 事务的隔离级别详解 脏读.不可重复读.可重复读.幻读详解 演示各种隔离级别产生的现象 关于隔离级别的选择 什么是事务? 数据库中的事务是指对数据库执行一批操作,这些操作最终要么全部执行成功,要么全部失败,不会存在部分成功的情况. 举个例子 比如A用户给B用户转账100操作,过程如下: 1

【强烈强烈推荐】《ORACLE PL/SQL编程详解》全原创(共八篇)--系列文章导航

原文:[强烈强烈推荐]<ORACLE PL/SQL编程详解>全原创(共八篇)--系列文章导航 <ORACLE PL/SQL编程详解> 系列文章目录导航 ——通过知识共享树立个人品牌. 本是成书的,但后来做其他事了,就无偿的贡献出来,被读者夸其目前为止最“实在.经典”的写ORACLE PL/SQL编程的文章-! 觉得对你有帮助,请留言与猛点推荐,谢谢. [推荐]ORACLE PL/SQL编程详解之一:PL/SQL 程序设计简介(千里之行,始于足下) 本篇主要内容如下:第一章 PL/S