二叉排序树综述
代码演示
二叉排序树实现
/*************************************************************************
> File Name: binary_search_tree.cpp
> Mail: 1136984246@qq.com
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#define SIZE(n) (n ? n->size : 0) // n == NULL, 表示叶子结点的孩子结点,数量为0
#define L(n) (n ? n->lchild : NULL) // n == NULL, 无左子树, n != NULL,返回左子树根节点
#define KEY(n) (n ? n->key : -1) // -1: 表示NULL
// 底层是二叉树,增加了size字段
typedef struct Node {
int key;
int size; // size 节点所在子树的节点数量
struct Node *lchild, *rchild;
} Node;
// 初始化:返回存储key值的结点Node
Node *getNewNode(int key) {
Node *p = (Node *)malloc(sizeof(Node));
p->key = key;
p->size = 1;
p->lchild = p->rchild = NULL;
return p;
}
// 更新子树(包括根结点)的结点数
void update_size(Node *root) {
root->size = SIZE(root->lchild) + SIZE(root->rchild) + 1;
return;
}
// 查找:返回int,表示有无
int search(Node *root, int key) {
if (root == NULL) return 0;
if (root->key == key) return 1; // 递归考虑边界条件
if (key < root->key) return search(root->lchild, key);
return search(root->rchild, key);
}
// 排名:查找排名第k位(从小往大)的元素,返回元素值[假设k合理]
int search_k(Node *root, int k) {
if (root == NULL) return -1;
if (SIZE(L(root)) == k - 1) return root->key; // 左子树的所有key值都比根节点key值小
// SIZE(L())有点像函数式编程
// 在左子树中
if (k <= SIZE(L(root))) {
return search_k(root->lchild, k);
}
// 在右子树中
return search_k(root->rchild, k - SIZE(L(root)) - 1); // 更新要找的k值
}
// ? 什么时候插入会改变根节点的地址
// 插入:根节点的地址可能会变化, 返回地址
Node *insert(Node *root, int key) {
if (root == NULL) return getNewNode(key); // 成功插入
if (root->key == key) return root; // 不可重复插入
if (key < root->key) root->lchild = insert(root->lchild, key); // ? 同样可以修改根节点
else root->rchild = insert(root->rchild, key);
update_size(root); // 更新节点数
return root;
}
// ? bug : 只有度为2的有前驱, 度为1的也有前驱(不在子树上)
// ? 用后驱可以做吗?
// 前驱(左子树中最大的节点):只是查找[度为2]的节点前驱
Node *predecessor(Node *root) {
Node *temp = root->lchild; // 左子树
while (temp->rchild) temp = temp->rchild; // 不断找右节点
return temp;
}
// 删除为key值得节点:可能会修改根节点
Node *erase(Node *root, int key) {
if (root == NULL) return NULL;
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) {
// 度为0(取&&)和度为1时,都可以这么操作
// 找到唯一子结点 [度为0是为NULL]
Node *temp = root->lchild ? root->lchild : root->rchild;
free(root); // 防止内存泄漏
return temp; // 这里要返回temp,其他情况返回root,因为要free root
} else {
// 度为2时[找前驱,覆盖根节点的值,删前驱->度最多为1]
Node *temp = predecessor(root);
root->key = temp->key;
root->lchild = erase(root->lchild, temp->key);
}
}
update_size(root); // 只修改root的,temp的不变
return root;
}
// 销毁 : 后序遍历
void clear(Node *root) {
if (root == NULL) return ;
clear(root->lchild);
clear(root->rchild);
free(root);
return;
}
// 节点展示方式
void print(Node *root) {
printf("(%d[%d], %d, %d)\n",
KEY(root), SIZE(root),
KEY(root->lchild), KEY(root->rchild)
);
return;
}
// 中序遍历
void output(Node *root) {
if (root == NULL) return;
output(root->lchild);
print(root);
output(root->rchild);
return;
}
// Top-k
void output_k(Node *root, int k) {
if (k == 0 || root == NULL) return ;
if (k <= SIZE(L(root))) {
// 直接输出左子树的前k位元素
output_k(root->lchild, k);
} else {
// 输出左子树的所有元素 + 根节点 + 右子树部分元素
output(root->lchild);
print(root);
output_k(root->rchild, k - SIZE(L(root)) - 1);
return ;
}
}
int main() {
int op, val;
Node *root = NULL; // 记得先定义根节点
while (~scanf("%d%d", &op, &val)) {
switch(op) {
case 0 : printf("search %d, result : %d\n----------\n", val, search(root, val)); break;
case 1 : root = insert(root, val); break;
case 2 : root = erase(root, val); break;
case 3 : {
printf("search k = %d, result : %d\n",
val, search_k(root, val));
printf("-------------\n");
} break;
case 4 :
printf("output top-%d element.\n", val);
output_k(root, val);
printf("-------------\n");
break;
}
// 插入或删除后输出树形结构
if (op == 1 || op == 2) {
if (op == 1) printf("insert %d\n", val);
else printf("delete %d\n", val);
output(root);
printf("-------------\n");
}
}
return 0;
}
随机数测试文件
/*************************************************************************
> File Name: rand.cpp
> Mail: 1136984246@qq.com
************************************************************************/
#include<iostream>
using namespace std;
int main() {
srand(time(0));
int op, val;
#define MAX_OP 20
for (int i = 0; i <MAX_OP; i++) {
switch (rand() % 7) {
// 非等概率操作 1:3:1 = 查:插:删
case 0 : printf("0 %d\n", rand() % 15); break;
case 1 :
case 2 :
case 3 : printf("1 %d\n", rand() % 15); break;
case 4 : printf("2 %d\n", rand() % 15); break;
case 5 : printf("3 %d\n", rand() % 5); break;
case 6 : printf("4 %d\n", rand() % 5); break;
}
}
return 0;
}
测试结果