搜索树
链表允许我们添加、删除、搜索元素,种种数据结构会在执行时申请必要的内存空间,便于管理动态集合。但是,在链表中搜索元素的算法复杂度为O(n)。相比之下,使用动态树结构能更加有效地添加、删除和搜索数据。
- 搜索树是一种可以进行插入、搜索、删除等操作的数据结构,可以用作字典或优先级队列。二叉搜索树属于最基本的搜索树。
二叉搜索树
- 顾名思义,二叉搜索树首先是二叉树,然后是搜索树。
- 对于二叉搜索树,一般满足以下性质:
左子结点小于等于父结点,右子结点大于等于父结点。
- 对于二叉搜索树来说,最为重要的就是如何插入,搜索和删除元素。
插入
- 二叉搜索树的插入很简单,先与根结点相比,比根节点小,就放在左子树里面,比根结点大,就放入右子树。在每一个子树中不断执行这一过程,直到需要插入的结点成为叶结点。对于插入来说,设树高为h,则算法复杂度为O(h)。
//使用链表建立二叉搜索树
struct Node {
int key;
Node *parent, *left, *right;
};
//声明根和尾,所有空都指向尾
Node *root;
void insert(int k){
Node *y = NULL;
Node *x = root;
Node *z;
//动态开辟空间并初始化
z = (Node*)malloc(sizeof(Node));
z->key = k;
z->left = NULL;
z->right = NULL;
while (x != NULL ){
//记录x的上一个并将x向下移动到底
y = x;
if (z->key < x->key){
x = x->left;
}
else {
x = x->right;
}
}
//将z和y连接
z->parent = y;
if (y == NULL) {
root = z;
}
else {
if (z->key < y->key){
y->left = z;
}
else {
y->right = z;
}
}
}
搜索
- 知道了如何插入,如何搜索也就很简单了。
- 与结点相比,如果要查找的值比它小,则搜索左子树,反之,则搜索右子树,不断重复这个过程直到找出或者搜索到达树底端。
- 同样,设树高为h,搜索的算法复杂度也为O(h)。
Node * find(Node *p, int x){
while (u != NULL && u->key != x){
if (x < u->key) u = u->left;
else u = u -> right;
}
return u;
}
删除
- 对于二叉搜索树来说,删除反而成了最为复杂的事情。
- 删除将遵循以下几条规则:
1.需要删除的是叶结点,直接删除。
2.需要删除的结点只有一棵子树,将这颗子树的根结点与删除结点的父结点连接。
3.需要删除的结点有两棵子树,可选择左子树的最大值,也可选择右子树的最小值。
void delNode(Node *u){
//如果要删除叶结点,直接删除
if (u->left == NULL && u->right == NULL){
if (u->parent->left == u)
u->parent->left = NULL;
else
u->parent->right = NULL;
free(u);
return;
}
//如果只有一棵子树,连接到这棵子树
else if (!u->left || !u->right){
if (u->left){
if (u->parent->left == u){
u->parent->left = u->left;
}
else{
u->parent->right = u->left;
}
}
else{
if (u->parent->left == u){
u->parent->left = u->right;
}
else{
u->parent->right = u->right;
}
}
free(u);
return;
}
//如果两棵子树都存在,则找出右子树的最小值,赋给此结点,然后递归删除最小值结点。
else{
Node *tmp = u->right;
while(tmp->left)
tmp = tmp->left;
u->key = tmp->key;
delNode(tmp);
free(tmp);
return;
}
}