前言
- 这个专栏将会用纯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;
}