您的位置:首页 > 理论基础 > 数据结构算法

数据结构查找(1)--二叉查找树

2012-12-15 21:57 302 查看
准备在期末之前解决掉红黑树。为了解决红黑树,首先学习二叉查找树,然后学习平衡的二叉查找树(这里的关键是树的旋转操作)。

最后在已经有上面知识的基础上,再来解决红黑树。至于B树家族,实在是太复杂,自己先了解下。等到自己实力增强的时候再去深入。

首先我们来看看二叉查找树:

下面是二叉查找树的定义:

二叉排序树或者是一棵空树;或者是具有如下特性的二叉树:

(1)若它的左子树不空,则左子树上

所有结点的值均小于根结点的值

(2)若它的右子树不空,则右子树上

所有结点的值均大于根结点的值

(3)它的左、右子树也都分别是二叉

排序树

给出一颗二叉查找树:



我们可以看到,根节点为8。左子树的所有节点的数据都比8小,右子树的所有节点的数据都比8大。

而且子树也满足下面的规律。

二叉查找树的概念我们了解起来应该不难。

我们用二叉链表的数据结构就可以来表示二叉查找树:

typedef struct BiTNode{
TElemType data;
struct BiTNode *lchild, *rchild, *parent;
}BiTNode, *BiTree;


下面是具体的基本算法实现。

1.查找,这里使用了递归来实现

Status SearchBST(BiTree T, KeyType key, BiTree f, BiTree &p)
//查找成功,  p指向该数据元素的结点
//查找不成功,p指向查找路径上访问的最后一个节点,f指向当前访问的结点的双亲,初始调用为NULL
{
if (T == NULL)
{
p = f;
return false;
}

else
{
if (T->data == key)
{
p = T;
return true;
}

else if (key < T->data)
{
f = T;
SearchBST(T->lchild, key, f, p);
}
else
{
f = T;
SearchBST(T->rchild, key, f, p);
}
}
}


2.插入

插入只有在查找失败的情况下面才进行。因为根据定义,二叉查找树不允许节点有相同的数据。

下面是插入算法

//二叉排序树的插入算法,只有在查找不成功的时候才进行插入
Status InsertBST(BiTree &T, TElemType e)
{
BiTree p;
BiTree s;
if (!SearchBST(T, e, NULL, p))
//不存在就插入
{
s = (BiTree) malloc (sizeof(BiTNode));
s->data = e;
s->lchild = s->rchild = NULL;
if (!p)
T = s;
else if (e < p->data)
p->lchild = s;
else
p->rchild = s;
return true;
}
else
return false;
}


3.中序遍历的前驱和后继

根据算法导论来好了。

为什么这里要强调中序遍历的前驱和后继?因为在下面的删除操作中我们会用到求前驱或者是求后继的操作。

那么我们在中序遍历的前驱和后继之前,我们先写两个查找最大节点和最小节点的算法。因为我们在 进行中序遍历的前驱和后继的时候要用到。

//最大关键字元素
BiTree BSTMaxmum(BiTree b)
{
BiTree p = b;
while (p->rchild)
p = p->rchild;
return p;
}

//最小关键字元素
BiTree BSTMinmum(BiTree b)
{
BiTree p = b;
while (p->lchild)
p = p->lchild;
return p;
}


有了这两个作为基础。现在我们来介绍下中序遍历的前驱和后继

我们以求后继作为例子:

求后继分为两种情况

1.如果结点的右子树非空,那么我们右子树的最左结点就是该节点的后继

2.如果结点的右子树为空,那么从x向上查找,直到遇到某个是其父结点的左儿子的结点为止。

下面以后继为例子,具体代码的实现。

//查找后继
BiTree SuccessorBST(BiTree b)
{
BiTree p = b;
//如果结点的右子树非空,那么右子树的最左结点就是该节点的后继
if (p->rchild)
return BSTMinmum(p->rchild);
//如果结点的右子树为空,那么从x向上查找,直到遇到某个是其父结点的左儿子的结点为止
BiTree y = p->parent;
while (y != NULL & p == y->rchild)
{
p = y;
y = y->parent;
}
return y;
}


前驱的方法与查找后继类似。具体代码如下:

//查找前驱
BiTree PredecessorBST(BiTree b)
{
BiTree p = b;
if (p->lchild)
return BSTMaxmum(p->lchild);

BiTree y = p->parent;
while (y != NULL & p == y->lchild)
{
p = y;
y = y->parent;
}
return y;
}


4.结点的删除

结点的删除时我们要保证删除后的结点仍然符合 二叉查找树的性质。

大家可以参考下面这个博客:
http://blog.csdn.net/danielhf/article/details/81210 http://www.cnblogs.com/aiyelinglong/archive/2012/03/27/2419972.html
删除分为三种

1. 要删除的节点没有子节点, 即它是叶节点



2. 要删除的节点有一个子节点



3. 要删除的节点有两个子节点



这里有个比较巧妙地方法,我们把第1种情况和第2种情况合并起来处理

如果左子树为空,就重接右子树;如果右子树为空,就重接左子树

最难的就是第三种情况了。

通过看图,我们来理解一下。

我们要删除 z 这个节点

1.我们通过中序遍历找到z的后继为y,我们已经知道y肯定没有左子树(如果这里不明白,再回上去看看吧)

2.把y结点的值赋给z

3.我们要删除y结点 ,让y的父亲结点成为y的右子树的结点 (自己唯一不明白的就是这个地方了)

最后来看看删除代码:

Status DeleteBST(BiTree &T, KeyType key)
{
//寻找删除的结点的位置
if (!T)
return false;

if (T->data == key)
{
Delete(T);
return true;
}
else if (key < T->data)
DeleteBST(T->lchild, key);
else
DeleteBST(T->rchild, key);
}
使用查找前驱的方法删除结点:

void Delete (BiTree &p)
{
BiTree q, s;
q = p;
//左子树为空,则重接右子树
if(!p->lchild)
{
p = p->rchild;
free(q);
}

//右子树为空,则重接左子树
else if(!p->rchild)
{
p = p->lchild;
free(q);
}

//左右子树都不为空
//找到前驱 (后继)
else
{
//s = PredecessorBST(p->lchild);
s = p->lchild;
while(!s->rchild)
{
q = s;
s = s->rchild;
}
//s为p的前驱
p->data = s->data;

if (q != p)
q->rchild = s->lchild;
else
q->lchild = s->lchild;
free(s);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: