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

准备在期末之前解决掉红黑树。为了解决红黑树,首先学习二叉查找树,然后学习平衡的二叉查找树(这里的关键是树的旋转操作)。
最后在已经有上面知识的基础上,再来解决红黑树。至于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);
	}
}






  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值