零、引言
如果一个数据集是循序存储,那么插入操作就是将记录放在表的末端,给表记录数加一即可;删除操作可以是删除后,后面的记录向前移,也可以是要删除的元素与最后一个元素互换,表记录数减一。这样整个数据集是无序的,插入和删除的效率很高,但是查找的效率就很低。
如果查找的数据集是有序线性表,并且是顺序存储的,查找就可以用折半、插值、斐波那契等查找法来实现。但是,因为数据的有序,在插入和删除操作上,就需要耗费时间。
下面将要介绍的这种数据结构,插入和删除的效率还不错,又可以比较高效率地实现查找算法。
一、二叉排序树
二叉排序树又称作二叉搜素、查找树,BST(Binary Sort\Search Tree),它可以是一颗空树,或者是具有下列性质的二叉树:
1.若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
2.若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
3.它的左、右子树也分别为二叉排序树。
构造一颗二叉排序树的目的是为了在保证有序的前提下(查找效率),提高插入和删除关键字的速度。
二、BST的相关操作
首先BST是二叉树,一般用二叉链表来表示,其结点的结构定义:
typedef struct BiTNode{
int data;
struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree
相关的操作主要有查找、插入和删除
其中删除操作较为复杂,可分为三种情况:
1.要删除的结点为叶子节点
这种情况最好处理,直接断开连接并释放内存即可。如下图所示:
2.要删除的结点只有左子树或者只有右子树
这种情况也较好处理,将结点删除后,将它的左子树或者右子树整个移动到删除结点的位置即可。如下图所示:
3.要删除的结点既有左子树也有右子树
如下图:
这时候,我们要找到子树中的一个结点来替代原结点而使得原二叉树性质不变,显然要找离“47”大小最近的点,也即“37”或者“48”。
它们正好是47的前驱和后继(中序遍历时)。因此比较好的办法就是,找到所删结点的直接前驱s(或者直接后继),用s来替换结点p,然后再删除此结点s。如下图所示:
下面是代码示例和注释(指针的指针操作)
//二叉排序树 Author:Feynman1999 language:C
//search insert and delete
#include<stdio.h>
#include<stdlib.h>
#define FALSE 0
#define TRUE 1
#define MAXSIZE 100
typedef struct BiTNode{
int data;
struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
//search
//f indicate T's parent,and initialization NULL
//*p save the last node prepare for inserting
int SearchBST(BiTree T,int key,BiTree f,BiTree *p)
{
if(!T){
*p=f;
return FALSE;
}
else if(key==T->data){
*p=T;
return TRUE;
}
else if(key<T->data)
return SearchBST(T->lchild,key,T,p);
else
return SearchBST(T->rchild,key,T,p);
}
//insert
void InsertBST(BiTree *T,int key)
{
BiTree p,s;
if(!SearchBST(*T,key,NULL,&p))//若树中没有相同元素
{
s=(BiTree)malloc(sizeof(BiTNode));
s->data=key;
s->lchild=s->rchild=NULL;//设置新元素s
if(!p) *T=s;//若树为空,将s设为根节点
else if(key<p->data) p->lchild=s;
else p->rchild=s;
}
else printf("sorry,there have been the same Node in the tree\n");
}
//DeleteBST's Subfunctions
void Delete(BiTree *p)
{
BiTree q,s;
if((*p)->rchild==NULL){//无右子树,以左子树替换
q=*p;
*p=(*p)->lchild;
free(q);
}
else if((*p)->lchild==NULL){//无左子树,以右子树替换
q=*p;
*p=(*p)->rchild;
free(q);
}
else{
q=*p;
s=(*p)->lchild;//这里取直接前驱,即左一下
while(s->rchild){//右到底
q=s;
s=s->rchild;
}
(*p)->data=s->data;//前驱替换删除结点
if(q!=*p) q->rchild=s->lchild;
else q->lchild=s->lchild;
free(s);
}
}
//Delete
void DeleteBST(BiTree *T,int key)
{
BiTree p;
if(SearchBST(*T,key,NULL,&p)){//找与key相等的元素
if(key==((*T)->data))
Delete(T);
else if(key<(*T)->data)
DeleteBST(&(*T)->lchild,key);
else
DeleteBST(&(*T)->rchild,key);
}
else printf("sorry,there is no the such Node\n");
}
//中序输出
void InOrder(BiTree T)
{
if(T)
{
InOrder(T->lchild);
printf("%d ",T->data);
InOrder(T->rchild);
}
else
return;
}
//前序输出
void PreOrder(BiTree T)
{
if(T)
{
printf("%d ",T->data);
PreOrder(T->lchild);
PreOrder(T->rchild);
}
else return ;
}
int main()
{
int num;
BiTree T=NULL;
int a[10]={48,72,56,93,7,14,28,4,58,63};
for(int i=0;i<10;++i){
InsertBST(&T,a[i]);
}
printf("当前的二叉树为:(中序)\n");
InOrder(T);
printf("\n");
printf("请输入你要插入的元素: ");
scanf("%d",&num);
InsertBST(&T,num);
printf("当前的二叉树为: (中序)\n");
InOrder(T);
printf("\n");
printf("请输入你要删除的元素:");
scanf("%d",&num);
DeleteBST(&T,num);
printf("当前的二叉树前序和中序序列为:\n");
PreOrder(T);
printf("\n");
InOrder(T);
printf("\n");
}
c++引用法代码
//二叉排序树 Author:Feynman1999 language:C
//search insert and delete
#include<stdio.h>
#include<stdlib.h>
#define FALSE 0
#define TRUE 1
#define MAXSIZE 100
typedef struct BiTNode{
int data;
struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
//search
//f indicate T's parent,and initialization NULL
//*p save the last node prepare for inserting
int SearchBST(BiTree &T,int key,BiTree f,BiTree &p)
{
if(!T){
p=f;
return FALSE;
}
else if(key==T->data){
p=T;
return TRUE;
}
else if(key<T->data)
return SearchBST(T->lchild,key,T,p);
else
return SearchBST(T->rchild,key,T,p);
}
//insert
void InsertBST(BiTree &T,int key)
{
BiTree p,s;
if(!SearchBST(T,key,NULL,p))//若树中没有相同元素
{
s=(BiTree)malloc(sizeof(BiTNode));
s->data=key;
s->lchild=s->rchild=NULL;//设置新元素s
if(!p) T=s;//若树为空,将s设为根节点
else if(key<p->data) p->lchild=s;
else p->rchild=s;
}
else printf("sorry,there have been the same Node in the tree\n");
}
//DeleteBST's Subfunctions
void Delete(BiTree &p)
{
BiTree q,s;
if(p->rchild==NULL){//无右子树,以左子树替换
q=p;
p=p->lchild;
free(q);
}
else if(p->lchild==NULL){//无左子树,以右子树替换
q=p;
p=p->rchild;
free(q);
}
else{
q=p;
s=p->lchild;//这里取直接前驱,即左一下
while(s->rchild){//右到底
q=s;
s=s->rchild;
}
p->data=s->data;//前驱替换删除结点
if(q!=p) q->rchild=s->lchild;
else q->lchild=s->lchild;
free(s);
}
}
//Delete
void DeleteBST(BiTree &T,int key)
{
BiTree p;
if(SearchBST(T,key,NULL,p)){//找与key相等的元素
if(key==(T->data))
Delete(T);
else if(key<T->data)
DeleteBST(T->lchild,key);
else
DeleteBST(T->rchild,key);
}
else printf("sorry,there is no the such Node\n");
}
//中序输出
void InOrder(BiTree T)
{
if(T)
{
InOrder(T->lchild);
printf("%d ",T->data);
InOrder(T->rchild);
}
else
return;
}
//前序输出
void PreOrder(BiTree T)
{
if(T)
{
printf("%d ",T->data);
PreOrder(T->lchild);
PreOrder(T->rchild);
}
else return ;
}
int main()
{
int num;
BiTree T=NULL;
int a[10]={48,72,56,93,7,14,28,4,58,63};
for(int i=0;i<10;++i){
InsertBST(T,a[i]);
}
printf("当前的二叉树为:(中序)\n");
InOrder(T);
printf("\n");
printf("请输入你要插入的元素: ");
scanf("%d",&num);
InsertBST(T,num);
printf("当前的二叉树为: (中序)\n");
InOrder(T);
printf("\n");
printf("请输入你要删除的元素:");
scanf("%d",&num);
DeleteBST(T,num);
printf("当前的二叉树前序和中序序列为:\n");
PreOrder(T);
printf("\n");
InOrder(T);
printf("\n");
}
三、结语
如果数组元素的次序是从小到大有序的,则二叉排序树就成了极端的右斜树,这样查找效率就大大降低。我们希望二叉排序树是比较平衡的,即其深度与完全二叉树相同。
这样我们就引申出另一个问题,如何让二叉排序树平衡的问题,即改进后的平衡二叉树(AVL树)。