二叉搜索树
思路
二叉排序树,二叉搜索树好像都行,原理应该都懂,比较基础,但要写出来还是有相当大的难度的。
查找
查找比较简单,基本都是一个while就解决。但查前驱与后继较难,可能需要上溯父节点。
- 任意键值
- 查最值
- 查某键值的前驱或后继
- 先序,中序,后序遍历
修改
修改键-值对,这个非常简单,查找出来即可。
增加
插入节点,比较简单。找到空节点插入即可
删除
比较复杂,分3种大情况,其中第3种又分两种情况
- 删除的节点没有左右孩子节点
- 删除的节点只有一个孩子节点
- 删除的节点有两个孩子节点
- 该节点的后继是右孩子
- 该节点的后继不是右孩子
# include <cstdio>
# include <iostream>
using namespace std;
/**************************/
struct BSTreeNode {
int key;
BSTreeNode * p; // 父节点
BSTreeNode * left; // 左孩子
BSTreeNode * right; // 右孩子
} * root;
/// 初始化
void init() {
root = NULL;
}
/// 新建节点
BSTreeNode * create_node(int key) {
BSTreeNode * p = new BSTreeNode;
p->key = key;
p->p = NULL;
p->left = NULL;
p->right = NULL;
return p;
}
/// 根据键查询
BSTreeNode * search_node(int key) {
BSTreeNode * x = root;
while(x!=NULL && key != x->key) {
if (key < x->key) x = x->left;
else x = x->right;
}
return x;
}
/// 查找某子树最小节点
BSTreeNode * search_minimum(BSTreeNode * p) {
if (p == NULL) return NULL;
while (p->left != NULL) p = p->left;
return p;
}
/// 查找某子树最大节点
BSTreeNode * search_maximum(BSTreeNode * p) {
if (p == NULL) return NULL;
while (p->right != NULL) p = p->right;
return p;
}
/// 查询节点前驱节点(节点)
BSTreeNode * search_predecessor(BSTreeNode * p) {
if (p->left != NULL) {
return search_maximum(p->left); // 拥有左子树,后继一定是左子树的最大节点
} else {
BSTreeNode * y = p->p;
while(y!=NULL && y->left==p) { // 找到高层节点中以p所在的树为右子树的树的根节点,即是前驱节点
p = y;
y = y->p;
}
return y;
}
}
/// 查询节点前驱节点(键值)
BSTreeNode * search_predecessor(int key) {
BSTreeNode * p = search_node(key);
if (p!=NULL) {
return search_predecessor(p);
}
return NULL;
}
/// 查找节点后继节点(节点)
BSTreeNode * search_successor(BSTreeNode * p) {
if (p->right != NULL) {
return search_minimum(p->right); // 拥有右子树,后继一定是右子树的最小节点
} else {
BSTreeNode * y = p->p;
while(y!=NULL && y->right==p) { // 找到高层节点中以p所在的树为左子树的树的根节点,即是后继节点
p = y;
y = y->p;
}
return y;
}
}
/// 查找节点后继(键值)
BSTreeNode * search_successor(int key) {
BSTreeNode * p = search_node(key);
if (p!=NULL) {
return search_successor(p);
}
return NULL;
}
/// 插入节点(节点)
void insert_node(BSTreeNode * s) {
BSTreeNode * x = root;
BSTreeNode * y = NULL;
while (x!=NULL) {
y = x;
if (s->key < x->key) x = x->left;
else x = x->right;
}
s->p=y;
if (y==NULL)
root = s;
else if (s->key < y->key)
y->left = s;
else
y->right = s;
}
/// 插入节点(键值)
bool insert_node(int key) {
BSTreeNode * f = search_node(key);
if (f!=NULL) return false; // 有重复的键
BSTreeNode * node = create_node(key);
insert_node(node);
return true;
}
/// 替换节点树(v替换u)
void transplant_node(BSTreeNode * u, BSTreeNode * v) {
if (u->p==NULL) {
root = v;
} else if (u->p->left == u) {
u->p->left = v;
} else {
u->p->right = v;
}
if (v!=NULL) {
v->p = u->p;
}
}
/// 删除节点(节点)
void delete_node(BSTreeNode * x) {
if (x->left == NULL) {
transplant_node(x, x->right);
} else if (x->right == NULL) {
transplant_node(x, x->left);
} else { // 左右孩子都存在的情况
BSTreeNode * y = search_minimum(x->right); // 找后继节点
if (y != x->right) { // 如果后继不是右孩子,需要变形。具体是将后继节点提为右子树的根节点
transplant_node(y, y->right); // 后继节点的左孩子一定不存在,右孩子取代后继节点
y->right = x->right;
y->right->p = y;
}
// 如果后继就是右孩子
transplant_node(x, y);
y->left = x->left; // 替换后还需要修改与左子树的父子关系
x->left->p = y;
}
delete x;
}
/// 删除节点(键值)
bool delete_node(int key) {
BSTreeNode * node = search_node(key);
if (node == NULL) return false; //键值不存在
delete_node(node);
return true;
}
/// 先序遍历
void preOrder(BSTreeNode * p) {
if (p == NULL) return;
printf("%d-", p->key);
preOrder(p->left);
preOrder(p->right);
}
/// 中序遍历
void inOrder(BSTreeNode * p) {
if (p == NULL) return;
inOrder(p->left);
printf("%d-", p->key);
inOrder(p->right);
}
/// 后序遍历
void postOrder(BSTreeNode * p) {
if (p == NULL) return;
postOrder(p->left);
postOrder(p->right);
printf("%d-", p->key);
}
int main() {
init();
insert_node(5);
insert_node(7);
insert_node(1);
insert_node(19);
insert_node(3);
preOrder(root);
printf("\n");
inOrder(root);
printf("\n");
postOrder(root);
printf("\n");
printf("%d->%d->%d\n", search_predecessor(5)->key, 5, search_successor(5)->key);
printf("%d->%d->%d\n", search_predecessor(7)->key, 7, search_successor(7)->key);
printf("%d->%d->%d\n", search_predecessor(3)->key, 3, search_successor(3)->key);
printf("%d, %d\n", search_minimum(root)->key, search_maximum(root)->key);
delete_node(5);
inOrder(root);
printf("\n");
return 0;
}