数据结构之二叉查找树

二叉查找树是按二叉树的结构来组织的,如下图所示。每个结点都是一个对象;结点中除了数据key外,还有三个指针p,left,right,分别指向父结点,左儿子,右儿子。

二叉查找树总是满足一下性质:

x是二叉查找树中的一个结点。如果y是x左子树的一个节点,则key[y]<=key[x]。如果y是右子树的一个结点,则key[y]>=key[x]。

根据二叉查找树的性质,可以用一个递归算法按排列顺序输出所有关键字:中序遍历。

//中序遍历
INORDER-TREE-WALK(x)
	if x≠NIL
		then INORDER-TREE-WALK(left[x])
				 pinrt key[x]
				 INORDER-TREE-WALK(right[x])
除此之外,还有前序遍历和后序遍历。显然遍历时间复杂度为O(n)。

对于二叉查找树,最常见的操作是查找某个关键字。树的高度为h,那么这些操作都可以在O(h)时间完成。

给定指向根的指针x和关键字k,返回包含关键字k的指针(如果存在),否则返回NIL。

TREE-SEARCH(x,k)
	if n=NIL or k=key[x]
		then return x
  if k<key[x]
  	then return TREE-SEARCH(letf[x],k)
  	else return TREE-SEARCH(right[x],k)
也可以用while循环来代替递归,一般来说非递归版本运行的更快一些

ITERATIVE-TREE-SEARCH(x,k)
	while x≠NIL and k≠key[x]
		if k<key[x]
			then x←letf[x]
			else x←right[x]
  return x
			
对于查找最大或最小关键字,更容易。根据二叉查找树的性质,从根开始,一直沿着左孩子找下去,直到遇到NIL为止,找到的就是最小的;一直沿着右孩子一直找下去,直到找到NIL为止,找到的就是最大的。

找最小关键字

TREE-MINIMUM(x)
	while left[x]≠NIL
		then x←left[x]
	return x
找最大关键字

TREE-MAXMUM(x)
	while right[x]≠NIL
		then x←right[x]
	return x

前趋和后继

在中序遍历下,输出的结点是从小到大排列好的。如果各个结点的关键字不相等,那么x其前趋就是刚好小于key[x]那个,其后继是刚好大于key[x]那个。根据二叉查找树的特殊结构,不用做对比,就可以直接找出其前趋和后继结点。

对于x其后继结点,如果right[x]不为空,那么其后继结点为TREE-MINIMUM(right[x]),如果x其右子树为空,且存在一个后继结点y,则y是x的最低祖先结点,且y的左儿子也是x的祖先(每个结点都是自己的祖先)。

找后继:

TREE-SUCCESSOR(x)
	if rithx[x]≠NIL
		then return TREE-MINIMUM(right[x])
	y←p[x]
	while y≠NIL and x=right[y]
	        do x←y
		 y←p[y]
	return y

同理,对于x找前趋,如果left[x]不为空,那么其前趋结点为TREE-MAXMUM(left[x]),如果其左子树为空,且存在一个前趋结点y,则y是x的最低祖先结点,且y的右孩子也是x的祖先

找前趋:

TREE-PREDECESSOR(x)
	if left[x]≠NIL
		 then return TREE-MAXMUM(left[x])
	y←p[x]
	while y≠NIL and x=left[x]
		do x←y
			y←p[y]
	return y


插入和删除

二叉查找树的插入和删除都会引起二叉查找树动态集合的变化,在动态集合的变化过程中,要保持二叉查找树的性质。

插入

将一个结点插入到二叉查找树中,总是将这个新结点插入到叶子结点(尽管插入方式不唯一),这样做简单很多。首先找到要插入的位置,然后将结点插入进去。

TREE-INSERT(T,z)
	y←NIL
	x←root(T)
	while x≠NIL
		do y←x//y是x的父结点
		if key[z]<key[x]
			then x←left[x]
			else x←right[x]
	p[z]←y
	if y=NIL//x是空树
		then root[T]←z
		else if key[y]>key[z]
			left[y]←z
			else right[y]←z 

将一个结点从二叉查找树中删除,相对来说比插入复杂。要分以下三种情况1、此结点是叶子结点,即左右子树为空。这种情况,只需更改其父结点的子指针。2此结点只有一个孩子结点,那么删掉这个结点,把孩子结点顶上去即可。3此结点既有左子树,又有右子树,那么则删除此节点的后继结点,把后继结点替代此结点。

TREE-DELETE(T,z)
	if left[z]=NIL or right[z]=NIL//找到要删除的结点y
		then y←z//如果左子树或右子树有空,那么要删除的结点就是z
		else y←TREE-SUCCESSOR(z)//如果左右子树都不为空,那么要删除的结点就是z的后继结点
	if left[y]≠NIL
		then x←left[y]
		else x←right[y]
	if x≠NIL//x不为空,那么x就是y的孩子结点
		then p[x]←p[y]
	if p[y]=NIL//如果y为根结点
		then  root[T]←x
		else if y=left[p[y]]//y是左子树
			    then left[p[y]]←x
			    else right[p[y]]←x
	if y≠z//删除掉的是z的后继
		then key[z]←key[y]
	return y



 



 



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值