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

前言

  • 这个专栏将会用纯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值不重复。

cpp 复制代码
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;
}

插入节点

采用递归简历树。

cpp 复制代码
// 插入过程建树
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树的删除):

cpp 复制代码
// 删除
// 这里采用:左节点,最右节点
// 利用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树的中序遍历是有序的

cpp 复制代码
// 中序有序
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);
}

总代码

cpp 复制代码
#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;
}
相关推荐
Hell with it20 分钟前
【bibtex4word】在Word中高效转换bib参考文献,Texlive环境安装bibtex4word插件
考研·学习方法
?abc!1 小时前
缓存(5):常见 缓存数据淘汰算法/缓存清空策略
java·算法·缓存
BioRunYiXue1 小时前
一文了解氨基酸的分类、代谢和应用
人工智能·深度学习·算法·机器学习·分类·数据挖掘·代谢组学
DanB241 小时前
Java笔记4
java·开发语言·笔记
Dddle11 小时前
C++:this指针
java·c语言·开发语言·c++
studyer_domi1 小时前
Matlab 234-锂电池充放电仿真
开发语言·matlab
yuanpan2 小时前
.net/C#进程间通信技术方案总结
开发语言·c#·.net
烟雨迷2 小时前
Linux环境基础开发工具的使用(yum、vim、gcc、g++、gdb、make/Makefile)
linux·服务器·学习·编辑器·vim
吃面不喝汤662 小时前
破解 Qt QProcess 在 Release 模式下的“卡死”之谜
开发语言·qt
不見星空2 小时前
2025年第十六届蓝桥杯软件赛省赛C/C++大学A组个人解题
c语言·c++·蓝桥杯