二叉排序树BST
二叉排序树(Binary Sort Tree)又称二叉查找树(Binary Search Tree),亦称二叉搜索树。
定义
二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
(4)没有键值相等的节点。
查找
步骤:

否则,若小于根结点的关键字值,递归查左子树。
若大于根结点的关键字值,递归查右子树。
若子树为空,查找不成功。
平均情况分析(在成功查找两种的情况下):
在一般情况下,设 P(n,i)为它的左子树的结点个数为 i 时的平均查找长度。如图的结点个数为 n = 6 且 i = 3; 则 P(n,i)= P(6, 3) = [ 1+ ( P(3) + 1) * 3 + ( P(2) + 1) * 2 ] / 6= [ 1+ ( 5/3 + 1) * 3 + ( 3/2 + 1) * 2 ] / 6
注意:这里 P(3)、P(2) 是具有 3 个结点、2 个结点的二叉分类树的平均查找长度。 在一般情况,P(i)为具有 i 个结点二叉分类树的平均查找长度。
P(3) = (1+2+2)/ 3 = 5/3
P(2) = (1+2)/ 2 = 3/2∴ P(n,i)= [ 1+ ( P(i) + 1) * i + ( P(n-i-1) + 1) * (n-i-1) ] / n
∴ P(n)=
P(n,i)/ n <= 2(1+I/n)lnn

因为 2(1+I/n)lnn≈1.38logn 故P(n)=O(logn)
插入删除
插入算法
首先执行查找算法,找出被插结点的父亲结点。
判断被插结点是其父亲结点的左、右儿子。将被插结点作为叶子结点插入。
若二叉树为空。则首先单独生成根结点。
注意:新插入的结点总是叶子结点。
//在二叉排序树中插入查找关键字key
void InsertBST(t,key)
{
if(t==NULL)
{
t=newBiTree;
t->lchild=t->rchild=NULL;
t->data=key;
return;
}
if(key<t->data)
InsertBST(t->lchild,key);
else
InsertBST(t->rchild,key);
}
//n个数据在数组d中,tree为二叉排序树根
void CreateBiTree(tree,d[],n)
{
tree=NULL;
for(i=0;i<n;i++)
InsertBST(tree,d[i]);
}
最小值二叉树c例程:
#include<stdio.h>
#include<malloc.h>
struct priorityqueue
{
intcapacity;
intsize;
structpriorityqueue*elements;
}*tryit;
structpriorityqueue*initialize(intmaxelements)
{
structpriorityqueue*h;
h=malloc(sizeof(structpriorityqueue));
h->elements=malloc(sizeof(int)*(maxelements+1));
h->capacity=maxelements;
h->size=0;
h->elements[0]=-23767;
returnh;
}
voidinsert(intx,structpriorityqueue*h)
{
inti;
for(i=++h->size;h->elements[i/2]>x;i/=2)
h->elements[i]=h->elements[i/2];
h->elements[i]=x;
}
intdeletemin(structpriorityqueue*h)
{
inti,child;
intminelement,lastelement;
minelement=h->elements[1];
lastelement=h->elements[h->size--];
for(i=1;i*2<=h->size;i=child)
{
child=i*2;
if(child!=h->size&&h->elements[child+1]<h->elements[child])
child++;
if(lastelement>h->elements[child])
h->elements[i]=h->elements[child];
else
break;
}
h->elements[i]=lastelement;
returnminelement;
}
main()
{
tryit=initialize(10);
insert(4,tryit);
insert(5,tryit);
insert(10,tryit);
insert(3,tryit);
printf("%d\n",deletemin(tryit));
printf("%d\n"deletemin(tryit));
printf("%d\n",deletemin(tryit));
printf("%d\n",deletemin(tryit));
getchar();
}
删除结点
在二叉排序树删去一个结点,分三种情况讨论:
-
若*p结点为叶子结点,即PL(左子树)和PR(右子树)均为空树。由于删去叶子结点不破坏整棵树的结构,则可以直接删除此子结点。
-
若*p结点只有左子树PL或右子树PR,此时只要令PL或PR直接成为其双亲结点*f的左子树(当*p是左子树)或右子树(当*p是右子树)即可,作此修改也不破坏二叉排序树的特性。
-
若*p结点的左子树和右子树均不空。在删去*p之后,为保持其它元素之间的相对位置不变,可按中序遍历保持有序进行调整,可以有两种做法:其一是令*p的左子树为*f的左/右(依*p是*f的左子树还是右子树而定)子树,*s为*p左子树的最右下的结点,而*p的右子树为*s的右子树;其二是令*p的直接前驱(或直接后继)替代*p,然后再从二叉排序树中删去它的直接前驱(或直接后继)-即让*f的左子树(如果有的话)成为*p左子树的最左下结点(如果有的话),再让*f成为*p的左右结点的父结点。在二叉排序树上删除一个结点的算法如下:
C代码
#defineStatusbool
StatusDelete(BiTree*);//必须先声明
StatusDeleteBST(BiTree&T,KeyTypekey)
{
//若二叉排序树T中存在关键字等于key的数据元素时,则删除该数据元素,并返回
//TRUE;否则返回FALSE
if(!T)
returnfalse;//不存在关键字等于key的数据元素
else
{
if(key==T->data.key)
{
//找到关键字等于key的数据元素
returnDelete(T);
}
elseif(key<T->data.key)
returnDeleteBST(T->lchild,key);
else
returnDeleteBST(T->rchild,key);
}
returntrue;
}
StatusDelete(BiTree&p)
{
//从二叉排序树中删除结点p,并重接它的左或右子树
if(!p->rchild)
{
//右子树空则只需重接它的左子树
q=p;
p=p->lchild;
deleteq;
}
elseif(!p->lchild)
{
//左子树空只需重接它的右子树
q=p;
p=p->rchild;
delete(q);
}
else
{
//左右子树均不空
q=p;
s=p->lchild;
while(s->rchild)
{
q=s;
s=s->rchild;
}//转左,然后向右到尽头
p->data=s->data;//s指向被删结点的“前驱”
if(q!=p)//以上循环至少执行了一次
q->rchild=s->lchild;
else
q->lchild=s->lchild;//重接*q的左子树
delete(s);
}
returntrue;
}
/**
*方法名称:delete()
*方法描述:删除节点
*@param采用递归的方式进行删除
*@returnString
*@Exception
*/
privatevoiddeleteNode(BinarySortTreep)
{
//TODOAuto-generatedmethodstub
if(p!=null)
{
//如果节点有左子树
/*1。若p有左子树,找到其左子树的最右边的叶子结点r,用该叶子结点r来替代p,把r的左孩子
作为r的父亲的右孩子。
2。若p没有左子树,直接用p的右孩子取代它。
*/
if(p.lChild!=null)
{
BinarySortTreer=p.lChild;
BinarySortTreeprev=p.lChild;
while(r.rChild!=null)
{
prev=r;
r=r.rChild;
}
p.data=r.data;
//若r不是p的左子树,p的左子树不变,r的左子树作为r的父节点的右孩子节点
if(prev!=r)
{
prev.rChild=r.lChild;
}
else
{
//若r是p的左子树,则p的左子树指向r的左子树
p.lChild=r.lChild;
}
}
else
{
p=p.rChild;
}
}
}
性能分析

浙公网安备 33010602011771号