二叉排序树及平衡二叉树的实现
二叉排序树
二叉排序树,又称为二叉查找树。它或者是一颗空树,或者具有下列性质 的二叉树。
•若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
•若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
•它的左、右子树也分别为二叉排序树。
构造一颗二叉排序树的目的,其实并不是为了排序,而是为了提高查找和>插入删除关键字的速度。不管怎么说,在一个有序数据集上的查找,速度总是要快于无序数据集的,而二叉排序树这种非线性的结构,也有利于插入和删除的实现。
插入和查找比较简单,在这里我就不详细说明了。
1、二叉排序树的插入
bool BinSortInsert(BTree *root,int num)
{
if(*root == NULL)
{
*root = (BTree)malloc(sizeof(BtNode));
(*root)->data = num;
(*root)->left = NULL;
(*root)->right = NULL;
return true;
}
if((*root)->data == num)
{
cout<<"要插入的结点已存在!"<<endl;
return false;
}
if((*root)->data > num)
{
BinSortInsert(&(*root)->left,num);
}
else
{
BinSortInsert(&(*root)->right,num);
}
}
2、二叉排序树的查找
BtNode* BinSortSearch(BTree root,int num)
{
BtNode* p = root;
if(p == NULL)
{
return NULL;//若为空则没有该节点
}
if(p->data == num)
{
return p;
}
if(p->data > num)
{
p = BinSortSearch(p->left,num);
}
else
{
p = BinSortSearch(p->right,num);
}
return p;
}
下面着重讲下删除操作:
首先,找到要删除的结点p,可以遍历,也可以调用上面已经写好的查找函数。
然后,p的情况分为三种:
(1)、p为叶子节点;直接删除p。
free(p);
(2)、p的左右子树之一为空;
左孩子为空,用q保存p结点,让p指向它的右孩子,再释放q;
BtNode *q;
q = p;
p = p->right;
free(q);
右孩子为空,用q保存p结点,让p指向它的左孩子,再释放q;
BtNode *q;
q = p;
p = p->left;
free(q);
(3)、p的左右子树都不为空;
左右孩子都不为空,则先找到p的直接前驱结点s,并记录s的父节点q;
BtNode *q,*s;
q->right = s->left;
p->data = s->data;
free(s);
3、二叉排序树的删除
void DeleteNode(BtNode **p)
{
BtNode *q,*s;
if((*p)->left == NULL && (*p)->right == NULL)
{
free(*p);
}
else if((*p) == NULL)
{
q = *p;
*p = (*p)->right;
free(q);
}
else if((*p) == NULL)
{
q = *p;
*p = (*p)->left;
free(q);
}
else
{
q = *p;
s = q->left;
while(s->right)
{
q = s;
s = s->right;
}
q->right = s->left;
(*p)->data = s->data;
free(s);
}
}
void BinSortDelete(BTree *root,int num)
{
BtNode * p = BinSortSearch(*root,num);
if(p == NULL)
{
return;
}
DeleteNode(&p);
/*
BtNode *p = *root;
if(p == NULL)
{
return;
}
if(p->data == num)
{
DeleteNode(&p);
}
else if (p->data > num)
{
BinSortDelete(&(p->left),num);
}
else
{
BinSortDelete(&(p->right),num);
}*/
}
4、二叉排序树整体实现
int main()
{
BTree BT = NULL;
int num;
cin>>num;
while(num != -1)
{
if(BinSortInsert(&BT,num))
{
cin>>num;
}
else
{
break;
}
}
BinSortDelete(&BT,32);
PrintBTree(BT);
return 0;
}
总结:二叉排序树的时间复杂度为O(logn),n为节点个数,log为树的深度,最坏的情况logn = n 即时间复杂度为O(n)优点在于采用链式存储(插入删除较方便)的同时,还能保证查找的时间复杂度不会太高。
但是它的缺点在于,随着插入的进行,树的形状可能会趋于不平衡,如下图所示。所以才有了AV树,通过插入后的旋转,改善了二叉排序树插入、删除导致的不平衡。