8.1 二叉排序树 —— C语言实现

系列文章目录

参考船说系列——数据结构与算法中的第八章内容。

  1. 二叉排序树
  2. AVL树
  3. 红黑树
  4. B-树

文章目录

  • 系列文章目录
  • 前言
  • 一、二叉排序树基础
  • 二、二叉排序树的操作
    • 2.1 插入
    • 2.2 删除
  • 三、代码演示
  • 总结
  • 参考文献


前言

数据结构 = 结构定义 + 结构操作
结构操作是用来维护结构性质的


一、二叉排序树基础

在这里插入图片描述二叉排序树(Binary Search Tree,BST)是一种二叉树,它具有以下性质:

  1. 每个节点都有一个值,且节点的值都不相同。
  2. 左子树中所有节点的值都小于该节点的值。
  3. 右子树中所有节点的值都大于该节点的值。
  4. 左右子树也分别为二叉排序树。

这些性质保证了二叉排序树的中序遍历是一个有序的序列。由于有序性,二叉排序树常被用于实现动态集合的数据结构,支持快速的查找、插入和删除操作。然而,如果插入的节点顺序不合理,可能导致二叉排序树退化成链表,影响其性能。

对于二叉排序树,平均情况下,查找、插入和删除操作的时间复杂度为O(log n),其中n为树中节点的数量。但是,在最坏情况下,二叉排序树可能退化成一个高度为n的链表,导致这些操作的时间复杂度为O(n)。

中序遍历:
在这里插入图片描述


二、二叉排序树的操作

2.1 插入

  1. 从根节点开始,比较要插入的值与当前节点的值。
    在这里插入图片描述

  2. 如果要插入的值小于当前节点的值,并且当前节点的左子节点为空,则将新节点插入为当前节点的左子节点;如果不为空,则继续向左子树遍历。(递归子问题)
    在这里插入图片描述

  3. 如果要插入的值大于当前节点的值,并且当前节点的右子节点为空,则将新节点插入为当前节点的右子节点;如果不为空,则继续向右子树遍历。(递归子问题)
    在这里插入图片描述

  4. 重复步骤2和步骤3,直到找到合适的位置插入新节点。
    在这里插入图片描述

2.2 删除

删除节点有几种情况要讨论:

  1. 删除叶子节点
    直接删除即可

  2. 删除出度为1的节点
    将其唯一子节点提升到要删除节点的位置

  3. 删除出度为2的节点
    在这里插入图片描述a. 20节点的前驱(前驱位置的出度只能是0或者1,因为前驱是之前节点中值最大的节点)替换20这个当前节点,这样20就成了它之前节点所在位置中的左子树中最大值的节点。这样删掉20节点就变成了一个删除出度为0或者1的问题了,解决办法就能参考前两种办法!!!
    b. 20节点的后继(后继位置的出度只能是0或者1,因为后继是之前节点中值最小的节点)替换20这个当前节点,这样20就成了它之前节点所在位置中的右子树中最小值的节点。这样删掉20节点就变成了一个删除出度为0或者1的问题了,解决办法就能参考前两种办法!!!

    有个容易找到前驱后继的方法:

    • 前驱是该节点的左子树一直往右,直到右节点为空的节点
    • 后继是该节点的右子树一直往左,直到左节点为空的节点

简单来说就是删除出度为2的节点,则可以选择将其右子树中最小的节点(后继)或左子树中最大的节点(前驱)替代要删除的节点,然后删除该最小或最大节点(变成了删除出度为0或者1的问题)。


三、代码演示

#include <stdio.h>
#include <stdlib.h>
#include <time.h>#define KEY(n) (n ? n->key : -1)typedef struct Node{int key;struct Node *lchild, *rchild;
} Node;Node *getNewNode(int key){Node *p = (Node *)malloc(sizeof(Node));p->key = key;p->lchild = p->rchild = NULL;return p;
}Node *insert(Node *root, int key){if (root == NULL) return getNewNode(key);if (key == root->key) return root; //重复的值不再插入if (key < root->key) root->lchild = insert(root->lchild, key);else root->rchild = insert(root->rchild, key);return root;   //返回根节点的地址
}Node *predecessor(Node *root){Node *temp = root->lchild;while (temp->rchild) temp = temp->rchild;return temp;
}Node *erase(Node *root, int key){if (root == NULL) return root;if (key < root->key) root->lchild = erase(root->lchild, key);else if (key > root->key) root->rchild = erase(root->rchild, key);else {if (root->lchild == NULL && root->rchild == NULL){free(root);return NULL;}else if (root->lchild == NULL || root->rchild == NULL){Node *temp = root->lchild ? root->lchild : root->rchild;free(root);return temp;}else {Node *temp = predecessor(root); //找到当前节点的前驱root->key = temp->key;root->lchild = erase(root->lchild, temp->key);}}return root;
}void clear(Node *root){if (root == NULL) return;clear(root->lchild);clear(root->rchild);clear(root);return;
}void output(Node *root){if (root == NULL) return;printf("(%d ; %d, %d)\n", KEY(root), KEY(root->lchild), KEY(root->rchild));output(root->lchild);output(root->rchild);return;
}void in_order(Node *root){if (root == NULL) return;in_order(root->lchild);printf("%d ", root->key);in_order(root->rchild);return;
}int main(){srand(time(0));#define MAX_OP 10Node *root = NULL;for (int i = 0; i < MAX_OP; i++){int key = rand() % 100;printf("insert key %d to BST\n", key);root = insert(root, key); //完成插入操作后新的BST根节点的地址}output(root);printf("in_order : ");in_order(root);printf("\n");int x;while (~scanf("%d", &x)){printf("erase %d from BST\n", x);root = erase(root, x);in_order(root);printf("\n");}return 0;
}

注意:在实际工程中,使用二叉排序树一般不会重复的key值,所以重复的数无须插入。

输出结果
在这里插入图片描述


总结

  1. 二叉排序树又称二叉搜索树,性质,中序遍历排序。
  2. 插入操作,按照其结构性质做一个递归即可。
  3. 删除操作,又分三种情况,最后一种情况可以转换成为前两种情况来实现。

参考文献

  1. 船说系列——数据结构与算法

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

spring高级篇(二)

1、Aware和InitializingBean Aware和InitializingBean都与Bean的生命周期管理相关。 Aware接口: 概念: Aware接口是Spring框架中的一个标记接口&#xff0c;它表示一个类能够感知到&#xff08;aware of&#xff09;Spring容器的存在及其特定的环境。Spring框架提供了多个Awar…

自然语言处理基础面试

文章目录 TF-IDFbag-of-wordsBert 讲道理肯定还得有Transformer&#xff0c;我这边先放着&#xff0c;以后再加吧。 TF-IDF TF&#xff08;全称TermFrequency&#xff09;&#xff0c;中文含义词频&#xff0c;简单理解就是关键词出现在网页当中的频次。 IDF&#xff08;全称…

【Pytorch】PytorchCPU版或GPU报错异常处理(10X~4090D)

Pytorch为CPU版或GPU使用报错异常处理 文章目录 Pytorch为CPU版或GPU使用报错异常处理0.检查阶段1. 在conda虚拟环境中安装了torch2.卸载cpuonly3.从tsinghua清华源安装不完善误为cpu版本4.用tsinghua清华源安装成cpu错误版本5.conda中torch/vision/cudatoolkit版本与本机cuda版…

Python基础学习之**kwargs

在Python编程中&#xff0c;**kwargs 是一个强大的工具&#xff0c;它允许我们在函数定义中接受任意数量的关键字参数。kwargs 是 "keyword arguments" 的缩写&#xff0c;实际上是一个字典&#xff0c;其中包含了传递给函数的所有关键字参数。本文将详细介绍 **kwar…

文本美学:text-image打造视觉吸引力

当我最近浏览 GitHub 时&#xff0c;偶然发现了一个项目&#xff0c;它能够将文字、图片和视频转化为文本&#xff0c;我觉得非常有趣。于是我就花了一些时间了解了一下&#xff0c;发现它的使用也非常简单方便。今天我打算和家人们分享这个发现。 项目介绍 话不多说&#xf…

用或非门构成的基本触发器

用或非门构成的基本触发器 电路组成 & 逻辑符号 注意&#xff1a;与用与非门构成的基本触发器相比&#xff0c;不仅 R 、 S R、S R、S 的几何位置不同&#xff0c;而且其上无反号&#xff0c;即高电平有效&#xff0c; Q Q Q 和 Q ‾ \overline{Q} Q​ 仍表示触发器的状…

最短路问题之Bellman-Ford,SPFA算法,例题 负环

Bellman-Ford算法&#xff1a; Bellman-Ford算法用于解决带有负权边的单源最短路径问题。其基本思想是通过不断地松弛边来逐步求解最短路径。算法的主要步骤如下&#xff1a; 初始化&#xff1a;将源点到各个顶点的距离初始化为无穷大&#xff0c;源点的距离初始化为0。重复更…

Linux使用Libevent库实现一个网页服务器---C语言程序

Web服务器 这一个库的实现 其他的知识都是这一个专栏里面的文章 实际使用 编译的时候需要有一个libevent库 gcc httpserv.c -o httpserv -levent实际使用的时候需要指定端口以及共享的目录 ./httpserv 80 .这一个函数会吧这一个文件夹下面的所有文件共享出去 实际的效果, 这…

关于电商独立站搭建中电商API数据采集接口的应用

搭建供应链系统时&#xff0c;您可能需要与电商平台进行集成&#xff0c;以实现订单管理、库存同步、物流跟踪等功能。以下是一些常见的电商接口&#xff0c;可以帮助您构建供应链系统&#xff1a; 1. **淘宝开放平台接口**&#xff1a;淘宝开放平台提供了丰富的接口&#xff…

【VSLAM】VINO-Mono安装部署与运行

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍VINO-Mono安装部署与运行。 学其所用&#xff0c;用其所学。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c;下次更新不迷…

【超级简单】vscode进入服务器的docker容器

前提 1、已经运行docker容器 2、已经用vscode链接服务器 在vscode中安装的插件 Dev Containers docker 在容器中安装的依赖 yum install openssh-server yum install openssh-clientsvscode进入服务器的docker容器 找到自己的容器&#xff0c;右键点击&#xff0c;找到…

【状态压缩 并集查找 图论】2157. 字符串分组

本文涉及知识点 状态压缩 并集查找 图论 LeetCode2157. 字符串分组 给你一个下标从 0 开始的字符串数组 words 。每个字符串都只包含 小写英文字母 。words 中任意一个子串中&#xff0c;每个字母都至多只出现一次。 如果通过以下操作之一&#xff0c;我们可以从 s1 的字母集…