首页 > 其他分享 >数据结构C语言描述11(图文结合)--二叉搜索树(BST树)的实现(数据采用KV存储形式进行封装)

数据结构C语言描述11(图文结合)--二叉搜索树(BST树)的实现(数据采用KV存储形式进行封装)

时间:2025-01-11 23:30:44浏览次数:3  
标签:11 Node -- data C语言 key rChild NULL root

前言

  • 这个专栏将会用纯C实现常用的数据结构和简单的算法;
  • 有C基础即可跟着学习,代码均可运行;
  • 准备考研的也可跟着写,个人感觉,如果时间充裕,手写一遍比看书、刷题管用很多,这也是本人采用纯C语言实现的原因之一;
  • 欢迎收藏 + 关注,本人将会持续更新。

文章目录

什么是二叉搜索树

二叉搜索树(Binary Search Tree,BST)是一种特殊的二叉树数据结构,其中每个节点的值都大于其左子树中的任何节点的值,且小于其右子树中的任何节点的值

它的特点使得在搜索、插入和删除操作上具有高效性。

以下是一些关于二叉搜索树的重要特性

  • 左子树中的所有节点的值都小于根节点的值。
  • 右子树中的所有节点的值都大于根节点的值。
  • 左右子树本身也是二叉搜索树。
  • 中序遍历有序。

由于这些特性,二叉搜索树可以用于高效地实现插入、搜索和删除操作。

  • 搜索操作可以在平均情况下以O(log n)的时间复杂度完成,其中n是树中节点的数量。
  • 然而,最坏情况下,树可能退化为链表,搜索操作的时间复杂度将变为O(n)
  • 插入操作的时间复杂度与搜索操作类似,平均情况下为O(log n),最坏情况下为O(n)
  • 删除操作的时间复杂度也是O(log n),最坏情况下为O(n)

BST书图示为:

在这里插入图片描述

代码实现

节点封装

节点封装,采用KV存储方式,数据在BST树中是存储K值这里规定K值不重复

typedef struct Data {
	int key;
	char data[DATAMAX];
}Data;

typedef struct Node {
	Data* data;
	struct Node* lChild;
	struct Node* rChild;
}Node;

Data* creata_data(Data data)
{
	Data* new_data = (Data*)calloc(1, sizeof(Data));
	assert(new_data);
	new_data->key = data.key;
	strncpy(new_data->data, data.data, DATAMAX);
	return new_data;
}

Node* create_node(Data data)
{
	Node* node = (Node*)calloc(1, sizeof(Node));
	assert(node);
	node->data = creata_data(data);
	assert(node->data);
	return node;
}

插入节点

采用递归简历树。

// 插入过程建树
void push_tree(Node** root, Data data)
{
	assert(root);

	// 树为NULL
	if (*root == NULL) {
		*root = create_node(data);
		return;
	}
	else {   // 树不为NULL
		if (data.key < (*root)->data->key) {
			push_tree(&(*root)->lChild, data);
		}
		else {   // > =默认也插在右边,但是一般这种kv存储,是不允许有重复值的
			push_tree(&(*root)->rChild, data);
		}
	}
}

删除节点

删除节点要注意,采用递归删除有5种情况,据图代码所示(清理可以参考代码随想录力扣题目解析:BST树的删除):

// 删除
// 这里采用:左节点,最右节点
// 利用BST树的特点
// 5种情况
Node* erase_tree(Node* root, Data data)
{
	if (root == NULL) {
		return root;
	}

	if (root->data->key == data.key) {
		if (root->lChild == NULL && root->rChild == NULL) {
			free(root);
			root = NULL;
			return root;
		}
		else if (root->lChild == NULL && root->rChild != NULL) {
			Node* t = root;
			root = root->rChild;
			free(t);
			t = NULL;
			return root;
		}
		else if (root->lChild != NULL && root->rChild == NULL) {
			Node* t = root;
			root = root->lChild;
			free(t);
			t = NULL;
			return root;
		}
		else {
			Node* t = root->lChild;

			while (t->rChild) {
				t = t->rChild;
			}

			t->rChild = root->rChild;
			Node* temp = root;
			root = root->lChild;

			free(temp);

			return root;
		}
	}

	if (root && root->data->key > data.key) root->lChild = erase_tree(root->lChild, data);
	if (root && root->data->key < data.key) root->rChild = erase_tree(root->rChild, data);
}

输出(中序遍历有序)

BST树的中序遍历是有序的

// 中序有序
void mid_travel(Node* root)
{
	if (root == NULL) {
		return;
	}

	mid_travel(root->lChild);
	printf("K: %d, V: %s\n", root->data->key, root->data->data);
	mid_travel(root->rChild);
}

总代码

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#include <string.h>

// 这里采用  kv 存储的方法,进行建立,  key也可以作为一个权重,具体情况需要结合业务

#define DATAMAX 20

typedef struct Data {
	int key;
	char data[DATAMAX];
}Data;

typedef struct Node {
	Data* data;
	struct Node* lChild;
	struct Node* rChild;
}Node;

Data* creata_data(Data data)
{
	Data* new_data = (Data*)calloc(1, sizeof(Data));
	assert(new_data);
	new_data->key = data.key;
	strncpy(new_data->data, data.data, DATAMAX);
	return new_data;
}

Node* create_node(Data data)
{
	Node* node = (Node*)calloc(1, sizeof(Node));
	assert(node);
	node->data = creata_data(data);
	assert(node->data);
	return node;
}

// 插入过程建树
void push_tree(Node** root, Data data)
{
	assert(root);

	// 树为NULL
	if (*root == NULL) {
		*root = create_node(data);
		return;
	}
	else {   // 树不为NULL
		if (data.key < (*root)->data->key) {
			push_tree(&(*root)->lChild, data);
		}
		else {   // > =默认也插在右边,但是一般这种kv存储,是不允许有重复值的
			push_tree(&(*root)->rChild, data);
		}
	}
}

// 删除
// 这里采用:左节点,最右节点
// 利用BST树的特点
// 5种情况
Node* erase_tree(Node* root, Data data)
{
	if (root == NULL) {
		return root;
	}

	if (root->data->key == data.key) {
		if (root->lChild == NULL && root->rChild == NULL) {
			free(root);
			root = NULL;
			return root;
		}
		else if (root->lChild == NULL && root->rChild != NULL) {
			Node* t = root;
			root = root->rChild;
			free(t);
			t = NULL;
			return root;
		}
		else if (root->lChild != NULL && root->rChild == NULL) {
			Node* t = root;
			root = root->lChild;
			free(t);
			t = NULL;
			return root;
		}
		else {
			Node* t = root->lChild;

			while (t->rChild) {
				t = t->rChild;
			}

			t->rChild = root->rChild;
			Node* temp = root;
			root = root->lChild;

			free(temp);

			return root;
		}
	}

	if (root && root->data->key > data.key) root->lChild = erase_tree(root->lChild, data);
	if (root && root->data->key < data.key) root->rChild = erase_tree(root->rChild, data);
}

// 中序有序
void mid_travel(Node* root)
{
	if (root == NULL) {
		return;
	}

	mid_travel(root->lChild);
	printf("K: %d, V: %s\n", root->data->key, root->data->data);
	mid_travel(root->rChild);
}

int main()
{
	Data data[10] = { {17, "yy"}, {9, "xx"}, {22, "zz"}, {2, "ll"}, {8, "tt"}, {33, "xh"}, {20, "tt"}, {18, "ee"}, {77, "zz"}, {10, "hh"} };

	Node* root = NULL;

	for (int i = 0; i < 10; i++) {
		push_tree(&root, data[i]);
	}

	mid_travel(root);
	
	int key = 22;
	
	Data temp = { 22, "tt" };
	
	erase_tree(root, temp);

	printf("*****************************\n");
	mid_travel(root);


	return 0;
}

标签:11,Node,--,data,C语言,key,rChild,NULL,root
From: https://blog.csdn.net/weixin_74085818/article/details/145084280

相关文章

  • ChatGPT-canvas进行学术写作是怎样的体验?全流程+提示词分享
    目录1.大纲框架✔2.正文✔        在这个信息爆炸的时代,如何高效地将思路转化为一篇条理清晰、内容丰富的文章?今天,让我们一起走进ChatGPT-Canvas的世界,探索它是如何巧妙地将大纲转化为正文内容的。ChatGPT-Canvas不仅仅是一个写作工具,它更像是一位聪明的写作伙伴,能......
  • 通义万相2.1:VBench榜单荣登第一!阿里通义万相最新视频生成模型,支持生成1080P长视频
    ❤️如果你也关注AI的发展现状,且对AI应用开发非常感兴趣,我会每日分享大模型与AI领域的最新开源项目和应用,提供运行实例和实用教程,帮助你快速上手AI技术,欢迎关注我哦!......
  • 瑞友天翼应用虚拟化系统 GetPwdPolicy SQL注入漏洞
    0x01阅读须知(免责声明)        技术文章仅供参考,此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等(包括但不限于)进行检测或维护参考,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失,均......
  • 代码随想录训练营第四十五天| 115.不同的子序列 583. 两个字符串的删除操作 72. 编辑
    115.不同的子序列题目链接:115.不同的子序列-力扣(LeetCode)讲解链接:代码随想录 hard确实不好直接说出来粘一下思路:(引自代码随想录)确定dp数组(dptable)以及下标的含义dp[i][j]:以i-1为结尾的s子序列中出现以j-1为结尾的t的个数为dp[i][j]。为什么i-1,j-1这么定义卡哥......
  • 用AI辅助开发的商标名称图片生成小工具!
    近年来AI大模型日新月异,普推知产商标老杨有时也用AI服务申请商标的企业,比如辅助起名和名称加字等,最近用AI辅助开发了一个商标的小工具“商标名称图片生成”。对于大家常用的文字商标,输入商标名称可以生成商标图片预览,背景是白色,颜色是黑色,字体是用的是可商用的开源字体黑......
  • 如何应对商标申请注册时的盲查期!
    前几天有个朋友说申请注册商标遇到盲查期了,两个商标名称就差几天申请,文字基本是一样的,商标申请注册时盲查期是商标无法通过的原因之一,这个也是没法解决的。商标提交申请注册后,大约20天左右到1个月后,在商标局的网站才会查出来商标信息,如何应对商标注册时的盲查期,商标......
  • 自己动手写CPU - 6
    自己动手写CPU_qq85058522的博客-CSDN博客CPU不加功能了,但汇编器可以有。下面写一个把汇编(助记符)翻译成机器码的小工具。Python熟些,就用它了。很简单,就是字符串替换。直接上代码。importsysiflen(sys.argv)!=2:print("usage:pythonassemblerxxx.asm")exi......
  • Linux 进程入门:带你走进操作系统的核心地带(2)
     ......
  • JAVA_JDBC(part one)
    MYSQL协议mysql协议官方文档mysql协议大体上分3部分payload长度(3字节)序列ID(1字节)payload包长度握手这一阶段主要交换客户端和服务器的能力判断是否需要使用SSL对客户端进行验证服务端挥手从3.21.0开始,使用Protocol::HandshakeV10数据结构如下1字节协......
  • 第7章 异常
    第7章异常7.1抛出异常​DO​​​:操作失败应该通过抛出异常的方式报告,而非通过返回错误码。‍​CONSIDER​:在代码遇到严重问题且无法继续安全地执行时,要调用​System.Enviroment.FailFast​​终止进程,而不是抛出异常。该方法会向Windows应用程序事件日志写入......