数据结构—二叉搜索树

二叉搜索树概念

      什么是二叉搜索树,在我看来二叉搜索树相对于二叉树最大的区别在于:二叉搜索树中的任意节点x,其左节点的关键字都小于x.key,其右节点的关键字都大于x.key。由该性质可以对二叉搜索树有三种遍历方法。

      先序遍历:先输出根节点的关键字,而后输出左子树的关键字,最后输出右子书的关键字。

      中序遍历:先输出左子树的关键字,而后输出根节点的关键字,最后输出右子树的关键字。

      后序遍历:先输出左子树的关键字,而后输出右子树的关键字,最后输出根节点的关键字。

如下图所示:

                                                                 

先序遍历:7,3,18,10,8,11,22,26

中序遍历:3,7,8,10,11,18,22,26

后序遍历:3,8,11,10,26,22,18,7

查询二叉树

二叉树的查询包括关键字查询,最大值、最小值查询,前驱和后驱的查询,其中关键字,最大值、最小值查询比较简单,重点介绍一下后驱的查询方法。
查找二叉树节点x的后驱会出现如下三种情况:
1.当节点x存在右孩子时,我们只需要查找以右孩子为树节点的树,关键字最小的节点,该节点则为节点x的后驱。
2.当节点x右孩子为空时,且该节点x为父节点的左孩子,则节点x的后驱为该父节点。
3.当节点x右孩子为空时,且该节点x为父节点的右孩子,则节点x的后驱为上层祖先。
伪代码如下:
TREE-SUCCESSOR(x)
if x.right!=NUL
return TREE_MINIMUM(x.right)
y=x.p
while y!=NULL and x==y.right
x=y
y=y.p
return y
tree_node* tree_successed(tree_node *x)		//查找节点x的后驱
{
	if(x->right!=NULL)						//情况一,假设存在右子树
	{
		return tree_min(x->right);			//查找右子树的最小值
	}
	while(x->parent->left!=x)				//情况2、3,假设没有右子树,则为父节点或者上层父节点
	{
		x=x->parent;
	}
	return x->parent;						
}

插入和删除

插入操作相对于删除操作简单,插入只需要找到关键字所处的位置,并修改相应指针即可,具体代码如下:
void tree_insert(tree_node *x,tree_node *z)  //二叉树插入一个节点
{
	tree_node *y=NULL;
	while(x!=NULL)
	{
		y=x;
		if(x->data>=z->data)
		{
			x=x->left;
		}
		else
		{
			x=x->right;
		}
	}
	z->parent=y;
	if(y->data>z->data)
	{
		y->left=z;
	}
	else
	{
		y->right=z;
	}
	z->left=NULL;
	z->right=NULL;
}

对于删除操作,是二叉搜索树的重点,也需要分成四种情况讨论:
1.节点x无孩子,即x为叶节点,直接将其用空节点替换就可以。
2.节点x存在一个孩子,则直接将孩子替换该节点,并修改相应节点属性。
3.节点x两个孩子都存在,但是节点x的后驱节点为节点x的右孩子,由二叉搜索树的性质可以知道后驱节点肯定没有左孩子,故直接将节点x的右孩子替换节点x,并修改相应节点(左孩子节点,父节点)的属性。
4.节点x两个孩子都存在,但后驱节点不为节点x的孩子,则通过后驱查找函数找到该节点y,由于后驱节点y没有左孩子,则可以用后驱节点y的右孩子替换后驱节点y,后驱节点y替换待删除节点x,并修改相应节点的属性。
具体情况如下所示:
情况1:
                                                                      
情况2:
                                                                                                
情况3:
                                                                                     
情况4:
                                                                                               
上图中所有删除节点都为z

删除具体代码如下:
void tree_transplant(tree_node *u,tree_node *v)				//节点v代替节点u
{
	 if(u->parent->left==u)
	{
		u->parent->left=v;
	}
	else
	{
		u->parent->right=v;
	}
	if(v!=NULL)							//防止指针v为空
	{
		v->parent=u->parent;
	}
	/*if(u->left!=NULL)			//容易自己指向自己,造成死循环
	{
		v->left=u->left;
		u->left->parent=v;
	}
	if(u->right!=NULL)
	{
		v->right=u->right;
		u->right->parent=v;
	}*/
}
void tree_delete(tree_node *x,tree_node *y)		//删除节点y
{
	tree_node *z,*successed;
	z=tree_search(x,y->data);
	if(z->left==NULL)									//左孩子为空
	{
		tree_transplant(z,z->right);
	}
	else if(z->right==NULL)								//右孩子为空
	{
		tree_transplant(z,z->left);
	}
	else												//存在两个孩子
	{
		if(z->right->left==NULL)						//情况3
		{
			tree_transplant(z,z->right);
			z->right->left=z->left;
			z->left->parent=z->right;
		}
		else											//情况4
		{
			successed=tree_successed(z);
			tree_transplant(successed,successed->right);
			successed->right->left=successed->left;		//将左孩子的属性更正
			successed->left->parent=successed->right;	
			tree_transplant(z,successed);				
			successed->left=z->left;
			z->left->parent=successed;
		}
		
	}
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值