前言
本文主要讲解了二叉搜索树,并且用C语言是实现了它的所有基本操作函数,给出了C语言代码
一、二叉搜索树
1.什么是二叉搜索树?
二叉搜索树是一颗二叉树,它是链表存储结构,同时满足特定的数据性质(见下)
顾名思义,二叉搜索树有很好的搜索性能,有诸多应用 它的操作函数有:
1.中序遍历(输出有序数据序列)
2.返回最大最小值
3.搜索值
4.返回某个结点的左右顺序统计量
5.插入,删除结点
2.二叉搜索树有什么优势?
(1)应用
1.作为字典存储数据,提供插入,删除,搜索功能
2.输出排好序的序列
3.作为优先队列
4.等等
(2)时间性能
一般情况下,它的所有操作函数的时间代价都是:
T
(
n
)
=
O
(
h
)
=
O
(
l
g
n
)
其中
h
为期望树高
,
h
=
O
(
l
g
n
)
T(n)=O(h)=O(lgn) \\其中h为期望树高,h=O(lgn)
T(n)=O(h)=O(lgn)其中h为期望树高,h=O(lgn)
3.二叉搜索树的数据特点
设x是二叉搜索树中的一个结点。如果y是x左子树中的一个结点,那么
y
.
k
e
y
?
x
.
k
e
y
y.key\leqslant x.key
y.key?x.key。如果y是x右子树中的一个结点,那么
y
.
k
e
y
?
x
.
k
e
y
y.key\geqslant x.key
y.key?x.key。
它本质上是维护一个局部的有序。这个局部的有序有点类似于堆。
(1)二叉搜索树与堆的数据性质的区别
1.二叉搜索树是将所有小于等于的结点放到左子树,所有大于等于的结点放到右子树
2.堆是在维护一个局部最值,即双亲结点永远要大于(最小堆是小于)孩子结点
具体可以参考文章: 《算法导论》学习(七)----堆排序和优先队列(C语言)
二、二叉搜索树的操作函数(C语言)
1.数据结构
数据结构包涵三个部分:
1.存储key值
2.指向左孩子结点的指针
3.指向有孩子结点的指针
4.指向父亲结点的指针
typedef struct STree
{
int key;
STree *p;
STree *left;
STree *right;
}ST;
2.中序遍历二叉搜索树
中序遍历的规则: 1.从横向来看:先遍历左子树,再遍历本结点,再遍历右子树 2.从竖向来看:先右下的叶子节点,再向上到根结点,再到坐下的叶子结点
二叉搜索树通过中序遍历,其结果是一个从小到大的有序数列。
(1)中序遍历
void inorder_tree_walk(ST *x)
{
if(x!=NULL)
{
inorder_tree_walk(x->left);
printf("%5d",x->key);
inorder_tree_walk(x->right);
}
else
{
return;
}
}
(2)打印遍历结果
void print_tree(ST *x)
{
inorder_tree_walk(x);
printf("\n");
}
3.查找数据
查找到该结点,就返回它的地址信息。 没有查到就返回NULL
(1)查找数据函数
ST *tree_search(ST *x,int k)
{
while(x!=NULL&&x->key!=k)
{
if(k<x->key)
{
x=x->left;
}
else
{
x=x->right;
}
}
return x;
}
(2)打印查找信息
void print_search(ST *x)
{
if(x==NULL)
{
printf("there is no data in tree\n");
}
else
{
printf("data is %5d,the address is 0x%x\n",x->key,x);
}
}
4.得到最大值
返回最大值的地址 根据二叉搜索树的性质,最大值是最右最下的叶子结点
ST *tree_max(ST *x)
{
while(x->right!=NULL)
{
x=x->right;
}
return x;
}
5.得到最小值
返回最小值的地址 根据二叉搜索树的性质,最小值是最左最下的叶子结点
ST *tree_min(ST *x)
{
while(x->left!=NULL)
{
x=x->left;
}
return x;
}
6.找到后继结点
后继结点的定义: 后继结点就是所有大于该结点的树节点中最小的那个
ST *tree_successor(ST *x)
{
if(x->right!=NULL)
{
return tree_min(x->right);
}
ST *y=x->p;
while(y!=NULL&&x==y->right)
{
x=y;
y=y->p;
}
return x;
}
7.找到前驱结点
前驱结点的定义: 前驱结点就是所有小于该结点的树节点中最大的那个
ST *tree_precursor(ST *x)
{
if(x->left!=NULL)
{
return tree_max(x->left);
}
ST *y=x->p;
while(y!=NULL&&x==y->left)
{
x=y;
y=y->p;
}
return x;
}
8.插入元素到树
ST *tree_insert(ST *x,int k)
{
ST *y=NULL;
ST *head=x;
if(x==NULL)
{
y=(ST *)malloc(sizeof(ST));
y->key=k;
y->left=NULL;
y->p=NULL;
y->right=NULL;
x=y;
return x;
}
while(x!=NULL)
{
y=x;
if(x->key>k)
{
x=x->left;
}
else
{
x=x->right;
}
}
x=(ST *)malloc(sizeof(ST));
x->key=k;
x->p=y;
x->left=NULL;
x->right=NULL;
if(y->key>k)
{
y->left=x;
}
else
{
y->right=x;
}
return head;
}
9.将一个子树替换为另外一个子树
该函数是为删除函数做准备的,删除函数需要大量调用 在删除函数中,它的功能是将u结点从树中分割出来,并且将以v结点为根结点的子树替换u结点的位置
void ctree_change(ST *x,ST *u,ST *v)
{
if(x==u)
{
x=v;
}
else if(u==u->p->left)
{
u->p->left=v;
}
else
{
u->p->right=v;
}
if(v!=NULL)
{
v->p=u->p;
}
}
10.删除结点
void tree_delet(ST *x,int k)
{
ST *k_p;
k_p=tree_search(x,k);
if(k_p==NULL)
{
printf("there is no k in tree\n");
}
else
{
if(k_p->left==NULL)
{
ctree_change(x,k_p,k_p->right);
free(k_p);
}
else if(k_p->right==NULL)
{
ctree_change(x,k_p,k_p->left);
free(k_p);
}
else
{
ST *temp=NULL;
temp=tree_min(k_p->right);
if(temp->p!=k_p)
{
ctree_change(x,temp,temp->right);
temp->right=k_p->right;
temp->right->p=temp;
}
ctree_change(x,k_p,temp);
temp->left=k_p->left;
temp->left->p=temp;
free(k_p);
}
}
}
三、二叉搜索树的C语言例子
1.实际执行结果
2.C语言代码
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define SIZE 10
#define LIM 100
typedef struct STree
{
int key;
STree *p;
STree *left;
STree *right;
}ST;
void inorder_tree_walk(ST *x)
{
if(x!=NULL)
{
inorder_tree_walk(x->left);
printf("%5d",x->key);
inorder_tree_walk(x->right);
}
else
{
return;
}
}
void print_tree(ST *x)
{
inorder_tree_walk(x);
printf("\n");
}
ST *tree_search(ST *x,int k)
{
while(x!=NULL&&x->key!=k)
{
if(k<x->key)
{
x=x->left;
}
else
{
x=x->right;
}
}
return x;
}
void print_search(ST *x)
{
if(x==NULL)
{
printf("there is no data in tree\n");
}
else
{
printf("data is %5d,the address is 0x%x\n",x->key,x);
}
}
ST *tree_max(ST *x)
{
while(x->right!=NULL)
{
x=x->right;
}
return x;
}
ST *tree_min(ST *x)
{
while(x->left!=NULL)
{
x=x->left;
}
return x;
}
ST *tree_successor(ST *x)
{
if(x->right!=NULL)
{
return tree_min(x->right);
}
ST *y=x->p;
while(y!=NULL&&x==y->right)
{
x=y;
y=y->p;
}
return x;
}
ST *tree_precursor(ST *x)
{
if(x->left!=NULL)
{
return tree_max(x->left);
}
ST *y=x->p;
while(y!=NULL&&x==y->left)
{
x=y;
y=y->p;
}
return x;
}
ST *tree_insert(ST *x,int k)
{
ST *y=NULL;
ST *head=x;
if(x==NULL)
{
y=(ST *)malloc(sizeof(ST));
y->key=k;
y->left=NULL;
y->p=NULL;
y->right=NULL;
x=y;
return x;
}
while(x!=NULL)
{
y=x;
if(x->key>k)
{
x=x->left;
}
else
{
x=x->right;
}
}
x=(ST *)malloc(sizeof(ST));
x->key=k;
x->p=y;
x->left=NULL;
x->right=NULL;
if(y->key>k)
{
y->left=x;
}
else
{
y->right=x;
}
return head;
}
void ctree_change(ST *x,ST *u,ST *v)
{
if(x==u)
{
x=v;
}
else if(u==u->p->left)
{
u->p->left=v;
}
else
{
u->p->right=v;
}
if(v!=NULL)
{
v->p=u->p;
}
}
void tree_delet(ST *x,int k)
{
ST *k_p;
k_p=tree_search(x,k);
if(k_p==NULL)
{
printf("there is no k in tree\n");
}
else
{
if(k_p->left==NULL)
{
ctree_change(x,k_p,k_p->right);
free(k_p);
}
else if(k_p->right==NULL)
{
ctree_change(x,k_p,k_p->left);
free(k_p);
}
else
{
ST *temp=NULL;
temp=tree_min(k_p->right);
if(temp->p!=k_p)
{
ctree_change(x,temp,temp->right);
temp->right=k_p->right;
temp->right->p=temp;
}
ctree_change(x,k_p,temp);
temp->left=k_p->left;
temp->left->p=temp;
free(k_p);
}
}
}
int main()
{
ST *t=NULL;
ST *temp;
int a[SIZE];
int i=0;
srand((unsigned)time(NULL));
for(i=0;i<SIZE;i++)
{
a[i]=rand()%LIM;
}
for(i=0;i<SIZE;i++)
{
printf("%5d",a[i]);
}
printf("\n");
for(i=0;i<SIZE;i++)
{
t=tree_insert(t,a[i]);
}
print_tree(t);
temp=tree_search(t,a[5]);
print_search(temp);
temp=tree_search(t,LIM);
print_search(temp);
temp=tree_min(t);
printf("min is %5d\n",temp->key);
temp=tree_max(t);
printf("max is %5d\n",temp->key);
temp=tree_precursor(t);
printf("the precursor of root data %5d is %5d\n",t->key,temp->key);
temp=tree_successor(t);
printf("the successor of root data %5d is %5d\n",t->key,temp->key);
tree_delet(t,a[5]);
print_tree(t);
return 0;
}
总结
本文的不妥之处请读者包涵指正
|