二叉搜索树(Binary Search Trees)《算法导论》笔记

12.1 什么是二叉搜索树

二叉搜索树是以一颗二叉树为原型加上某些约束而创建的一颗树,如下图所示:
在这里插入图片描述每个节点除了key卫星数据外,还包含属性leftrightp,它们分别指向左子结点、右子结点、父结点。如果某个结点的子结点或者父结点不存在,则将相应的属性的值设为NIL。根结点(从上往下数第一个结点)是树中唯一的父指针为NIL的结点。

二叉搜索树中的key值必须满足以下约束:
设x是二叉搜索树中的某个结点。则,x的键值要:
1. 不小于它的左子树中的任意一个结点的键值
2. 不大于它的右子树中的任意一个结点的键值

总结:不小左不大右

12.2 查询二叉搜索树

二叉搜索树不仅支持 SEARCH 操作,还支持诸如 MINIMUM、MAXIMUM、SUCCESSOR、PREDECESSOR的查询操作。本节将会用伪代码实现这些操作,以及说明它们的执行时间为O(h),其中h为树的高度(即从根结点出发到叶结点的最长简单路径的边的数目)。

查找

递归版本

TREE-SEARCH(x,k)
	if x == NIL or k == x.key  //递归出口
		return x		
	if k < x.key
		return TREE-SEARCH(x.left,k)
	else 
		return TREE-SEARCH(x.right,k)

非递归版本

TREE-SEARCH(x,k)
	while x != NIL and k != x.key
		//向下更新x
		if k < x.key
			x = x.left
		else
			x = x.right
			
	return x

结合不小左不大右的约束,很容易得出以上代码的原理。由于代码中的x均从结点x开始沿某条简单路径向下遍历二叉搜索树,因此代码的运行时间为O(h)

最大关键字元素和最小关键字元素

根据上面的不小左不大右的约束条件去观察一棵非空的二叉搜索树可以发现,这棵树的最小关键字元素在树的左下角,而最大关键字元素则在树的右下角
在这里插入图片描述下面给出代码,这里假设x不为NIL

找最小

TREE-MINIMUM(x)
	while x.left != NIL
		x = x.left
	return x

找最大

TREE-MAXIMUN(x)
	while x.right != NIL
		x = x.right
	return x

观察可得,两代码的运行时间均为O(h)。

后继与前驱

给定一棵二叉搜索树的结点,有时需要搜索它的后继结点或者前驱结点。

假设某棵二叉搜索树的键值都各不相同,则:
一个结点x的前驱是树中小于x.key的最大关键字的结点
一个结点x的后继是树中大于x.key的最小关键字的结点

下面先给出TREE-SUCCESSOR找后继的伪代码,然后对它进行分析。

TREE-SUCCESSOR(x)
	if x.right != NIL
		return TREE-MINIMUM(x.right)
	y = x.p
	while y != NIL and x != y.left   //寻找 右拐处
		//向上更新x和y
		x = y  
		y =x.p
	return y

分析:
如果x的右子树不为空,则在右子树中找到最小键值结点y,此时y则是x的后继。

如果如果x的右子树是空的,则只能往上搜索后继,且后继往往出现在如下图所示的往上走的“右拐”处。

在这里插入图片描述

结合不小左不大右的约束去分析为什么后继出现在往上走的“右拐”处。

如果往上搜没有找到这样的右拐处且遇到根结点,则返回NIL,代表x的后继不存在。

由于代码向上沿着简单路径遍历搜索树,因此运行时间为O(h)

仿照上面的TREE-SUCCESSOR的代码,我们很容易写出找前驱TREE-PREDECESSOR的代码。

至此,我们可以得出一个定理:
定理12.2 在一棵高度为h的二叉搜索树上,能在O(h)内完成SEARCH、MINIMUM、MAXIMUM、SUCCESSOR、PREDECESSOR的查询操作。

12.3 插入和删除

由于插入和删除操作会新增或者删除元素,这样会破坏搜索树原有的结构,因此需要对树的结构进行分析以作适当的调整。

插入

要将一个新的键值为v的元素z插入到一棵二叉搜索树T中,需要调用下面的TREE-INSERT。该过程以某个新结点z作为输入,其中z.key = v,z.left = NIL,z.right = NIL。之所以将z的左右子孩指针置空是因为在插入操作结束后,z将成为该树的某个叶结点。
伪代码

TREE-INSERT(T,z)
	y = NIL
	x = T.root
	while x != NIL  //往下做遍历寻找插入点
		y = x  //提前更新y指向 下面新x 的父结点
		//根据键值向下更新x
		if z.key < x.key
			x = x.left
		else
			x = x.right
	z.p = y   
	if y == NIL    //证明没有进入上面的循环体 即树是空的
		T.root = z
	else if z.key < y.key
		y.left = z
	else
		y.right = z 

分析只要抓住不小左不大右的约束进行即可。由于x也是沿着某条简单路径向下遍历,因此算法的执行时间为O(h)。

删除

删除操作的核心在于在找到能够替换掉待删元素位置而又不破坏不小左不大右约束的方法。假设待删元素为z,下面将列出删除操作将要面对的所有情形:
(a)z的左子孩为NIL
在这里插入图片描述此时只需要将z的右子孩r移植到z原来的位置上就可以轻松完成删除元素z的操作而又不破坏搜素树不小左不大右的约束

右子孩是否为空这里不关心

(b)z的右子孩为NIL
在这里插入图片描述与(a)类似,只需将z的左子孩l移植到z原来的位置即可。

当z的左右子孩均不为空时,要找到一个元素y去替代z,这个元素必须维持约束条件,而z的右子树中的z的后继y刚好能够满足这个条件。

这是一个初学者比较难理解的点,要结合不小左不大右的约束和等效思想去分析下面的操作为什么能够满足约束条件。

因此只需要在z的右子树中找到z的后继y,然后将它放到z的位置上并做一些简单的处理即可满足约束条件,具体见下:

(c)z的左右子孩均不为空且z.right == y
在这里插入图片描述
此时只需将以后继y为根的子树移植到z的位置,然后将z原来的左子孩l拼接成为y的左子孩即可。

后继y的的左子孩一定是空的,这是它的一个特点

(d)z的左右子孩均不为空且z.right != y
在这里插入图片描述这种情况将分3步处理。如下图所示

在这里插入图片描述1.找到后继y,将以y.right即x为根的子树移植到y所在的位置
2.将后继y移至以z.right即r为根的子树的上方构成一棵以y为根的新的子树

3.将2中新构建的子树移植到z的位置,然后将z原来的左子孩l拼接到y的左子孩的位置

当经历了步骤1,2后,情形(d)将会转化为情形(c),因此在代码实现中只需要将d的步骤1,2看做是预处理便能将情形(c),(d)合并为一个大的情形。

至此删除操作的所有情形都已经列出并提供了相应的解决方案,下面利用伪代码实现一下这个过程。

首先定义一个辅助函数TRANSPLANT用于移植操作。这里假设u(不可为空)为被替换的结点,v(可以是NIL)为用于移植操作的结点,简单的说就是用v替换掉u。

TRANSPLANT(T,u,v)
	if u.p == NIL           //u为根结点
		T.root = v
	else if u == u.p.left   //u为某结点的左子孩
		u.p.left = v
	else                   //u为某结点的右子孩
		u.p.right = v
	if v != NIL        
		v.p = u.p

观察可得,它的运行时间为O(1)

下面是删除操作的伪代码

TREE-DELETE(T,z)
	if z.left == NIL       //情形a
		TRANSPLANT(T,z,z.right)    
	else if z.right ==NIL   //情形b
		TRANSPLANT(T,z,z.left)
	else         //情形 c d
		y = TREE-MINIMUM(z.right)  //后继y
		if y.p != z   //预处理
			TRANSPLANT(T,y,y.right) 
			y.right = z.right
			y.right.p = y
	    TRANSPLANT(T,z,y) 
	    y.left = z.left
	    y.left.p = y

以上代码没有考虑z的内存释放的问题,如果需要作手动内存释放的话,还需要添加相应的代码。

观察代码结合TRANSPLANT操作的运行时间为O(1),TREE-MINIMUM的运行时间为O(h),可得TREE-DELETE操作的运行时间为O(h)。

至此,可以得到以下定理:
定理12.3 在一棵高度为h的二叉搜索树上,实现动态集合操作INSERT和DELETE的运行时间均为O(h)。

12.4 随机构建二叉搜索树

本节内容需要用到的数学知识多一点,也比较枯燥,可以选择性跳过。

根据前面的推导,我们知道二叉搜索树的每个操作的运行时间都可控制在O(h)内,这里h为树的高度。但如果一棵搜索树的创建过程中既有插入操作又有删除操作,此时h的高度却是难以预料的。

例如,将n个不同关键字的元素按照升序的次序调用TREE-INSERT插入到一棵二叉搜索树中,则这课树的最终形状是一条长度为n-1的链表。此时算法的基本操作时间就变为O(h)= O(n),这就与普通链表的操作时间一样了。

为了利用好二叉搜索树这个结构,必须要对树的高度h进行控制。这里我们首先定义含n个关键字的一棵随机构建二叉搜索树的过程为:
1.随机挑选出由n个不同关键字元素组成的n!个排列中的一个排列作为输入序列。
2.根据1中的输入序列依次调用TREE-INSERT插入从而构建出一棵二叉搜索树。

下面将会证明以下定理
定理12.4 一棵有n个不同关键字的随机构建二叉搜索树的期望高度为O(lgn)。
证明
首先定义三个随机变量:

  • 定义Xn为一棵由n个不同关键字随机构建二叉搜索树的高度。
  • 定义 Y n = 2 X n Y_n=2^{X_n} Yn=2Xn为指数高度。
  • 在一棵由n个不同关键字随机构建的二叉搜索树中,定义Rn用于表示根结点x的键值x.key在关键字集合中的顺序统计量。比如在一棵以1到9为键值随机构建的二叉搜索树中,如果根结点的键值为2,则R9= 2

假设某棵由n个不同关键字随机构建二叉搜索树Rn=i,则根据不小左不大右的约束可知,根的左子树是一棵由i-1个元素随机构建的二叉搜索树,根的右子树是一棵由n-i个元素随机构建的二叉搜索树。因为二叉搜索树的高度比根的两棵搜索子树中的较高的那棵的高度高1,即:
X n = max ⁡ ( X i − 1 , X n − i ) + 1 X_{n}= \max \left(X_{i-1}, X_{n-i}\right)+1 Xn=max(Xi1,Xni)+1
进而得
Y n = 2 ⋅ max ⁡ ( Y i − 1 , Y n − i ) Y_{n}=2 \cdot \max \left(Y_{i-1}, Y_{n-i}\right) Yn=2max(Yi1,Yni)
接下来定义指示器随机变量 Z n , 1 , Z n , 2 , … , Z n , n Z_{n, 1}, Z_{n, 2}, \ldots, Z_{n, n} Zn,1,Zn,2,,Zn,n,其中 Z n , i = I { R n = i } Z_{n, i}=\mathrm{I}\left\{R_{n}=i\right\} Zn,i=I{Rn=i}。因为Rn的值等可能地为{1,2,…,n}中的一个,因此有
Pr ⁡ { R n = i } = 1 / n \operatorname{Pr}\left\{R_{n}=i\right\}=1 / n Pr{Rn=i}=1/n
其中i = 1,2,…,n。
故由引理5.1得
E [ Z n , i ] = 1 / n \mathrm{E}\left[Z_{n, i}\right]=1 / n E[Zn,i]=1/n
其中i=1,2,…,n。由于 Z n , i Z_{n, i} Zn,i只有一个的值为1,其余所有的值均为0,因此有
Y n = ∑ i = 1 n Z n , i ( 2 ⋅ max ⁡ ( Y i − 1 , Y n − i ) ) Y_{n}=\sum_{i=1}^{n} Z_{n, i}\left(2 \cdot \max \left(Y_{i-1}, Y_{n-i}\right)\right) Yn=i=1nZn,i(2max(Yi1,Yni))
下面将证明E[Yn]是n的一个多项式,最终借此推出:
E [ X n ] = O ( l g n ) E[X_n] = O(lgn) E[Xn]=O(lgn)
另外要说明的一点是变量 Z n , i Z_{n, i} Zn,i是独立于变量 Y i − 1 , Y n − i Y_{i-1}, Y_{n-i} Yi1,Yni的。简单来说是因为根结点的键值不会影响根的随机构建子树的高度,详见书中的解释。
利用这个独立性可得:
E [ Y n ] = E [ ∑ i = 1 n Z n , i ( 2 ⋅ max ⁡ ( Y i − 1 , Y n − i ) ) ] = ∑ i = 1 n E [ Z n , i ( 2 ⋅ max ⁡ ( Y i − 1 , Y n − i ) ) ] = ∑ i = 1 n E [ Z n , i ] E [ 2 ⋅ max ⁡ ( Y i − 1 , Y n − i ) ] = ∑ i = 1 n 1 n ⋅ E [ 2 ⋅ max ⁡ ( Y i − 1 , Y n − i ) ] = 2 n ∑ i = 1 n E [ max ⁡ ( Y i − 1 , Y n − i ) ] ≤ 2 n ∑ i = 1 n ( E [ Y i − 1 ] + E [ Y n − i ] ) \begin{aligned} \mathrm{E}\left[Y_{n}\right] &=\mathrm{E}\left[\sum_{i=1}^{n} Z_{n, i}\left(2 \cdot \max \left(Y_{i-1}, Y_{n-i}\right)\right)\right] \\ &=\sum_{i=1}^{n} \mathrm{E}\left[Z_{n, i}\left(2 \cdot \max \left(Y_{i-1}, Y_{n-i}\right)\right)\right] \\ &=\sum_{i=1}^{n} \mathrm{E}\left[Z_{n, i}\right] \mathrm{E}\left[2 \cdot \max \left(Y_{i-1}, Y_{n-i}\right)\right] \\ &=\sum_{i=1}^{n} \frac{1}{n} \cdot \mathrm{E}\left[2 \cdot \max \left(Y_{i-1}, Y_{n-i}\right)\right] \\ &=\frac{2}{n} \sum_{i=1}^{n} \mathrm{E}\left[\max \left(Y_{i-1}, Y_{n-i}\right)\right] \\ & \leq \frac{2}{n} \sum_{i=1}^{n}\left(\mathrm{E}\left[Y_{i-1}\right]+\mathrm{E}\left[Y_{n-i}\right]\right) \end{aligned} E[Yn]=E[i=1nZn,i(2max(Yi1,Yni))]=i=1nE[Zn,i(2max(Yi1,Yni))]=i=1nE[Zn,i]E[2max(Yi1,Yni)]=i=1nn1E[2max(Yi1,Yni)]=n2i=1nE[max(Yi1,Yni)]n2i=1n(E[Yi1]+E[Yni])

上面式子的最后一个和式可进一步合并,因为 E [ Y 0 ] , E [ Y 1 ] , … , E [ Y n − 1 ] \mathrm{E}\left[Y_{0}\right], \mathrm{E}\left[Y_{1}\right], \ldots, \mathrm{E}\left[Y_{n-1}\right] E[Y0],E[Y1],,E[Yn1]这些项都在和式中出现了两次。故有
E [ Y n ] ≤ 4 n ∑ i = 0 n − 1 E [ Y i ] \mathrm{E}\left[Y_{n}\right] \leq \frac{4}{n} \sum_{i=0}^{n-1} \mathrm{E}\left[Y_{i}\right] E[Yn]n4i=0n1E[Yi]

下面将用归纳法证明
E [ Y n ] ≤ 1 4 ( n + 3 3 ) \mathrm{E}\left[Y_{n}\right] \leq \frac{1}{4}\left(\begin{array}{c} n+3 \\ 3 \end{array}\right) E[Yn]41(n+33)

在求解的过程中利用了以下恒等式
∑ i = 0 n − 1 ( i + 3 3 ) = ( n + 3 4 ) \sum_{i=0}^{n-1}\left(\begin{array}{c} i+3 \\ 3 \end{array}\right)=\left(\begin{array}{c} n+3 \\ 4 \end{array}\right) i=0n1(i+33)=(n+34)

可以利用数学归纳法证明此恒等式

下面开始数学归纳:
首先定义Y0=0,Y1=1

定义它们的值是有一定依据的。由于Y0在整个过程中都是人为定义的,它没有什么背景含义,因此为方便起见可以将它设为0。Y1是有背景含义的,当一棵随机创建的二叉搜索树只含一个元素时,它的树高为零(读者如果困惑可以查一下关于二叉树树高的定义)。而树高为零直接导致 Y 1 = 2 X 1 = 2 0 = 1 Y_1 = 2^{X_1}=2^0=1 Y1=2X1=20=1

对于初始情况我们有
E [ Y 0 ] = 0 ≤ ( 1 / 4 ) ( 3 3 ) = 1 / 4 \mathrm{E}\left[Y_{0}\right] = 0 \leq(1 / 4)\left(\begin{array}{l} 3 \\ 3 \end{array}\right)=1 / 4 E[Y0]=0(1/4)(33)=1/4

E [ Y 1 ] = 1 ≤ ( 1 / 4 ) ( 1 + 3 3 ) = 1 \mathrm{E}\left[Y_{1}\right]=1 \leq(1 / 4)\left(\begin{array}{c} 1+3 \\ 3 \end{array}\right)=1 E[Y1]=1(1/4)(1+33)=1
对于递归情形
在这里插入图片描述至此我们证得
E [ Y n ] ≤ 1 4 ( n + 3 3 ) E[Y_n] \leq \frac{1}{4}\left(\begin{array}{c} n+3 \\ 3 \end{array}\right) E[Yn]41(n+33)
由于 f ( x ) = 2 x f(x) = 2^x f(x)=2x是凸函数,利用Jensen不等式(C.26)得
2 E [ X n ] ≤ E [ 2 X n ] = E [ Y n ] \begin{aligned} 2^{\mathrm{E}\left[X_{n}\right]} & \leq \mathrm{E}\left[2^{X_{n}}\right] \\ &=\mathrm{E}\left[Y_{n}\right] \end{aligned} 2E[Xn]E[2Xn]=E[Yn]
结合上面的不等式得

2 E [ X n ] ≤ 1 4 ( n + 3 3 ) = 1 4 ⋅ ( n + 3 ) ( n + 2 ) ( n + 1 ) 6 = n 3 + 6 n 2 + 11 n + 6 24 \begin{array}{l} 2^{\mathrm{E}\left[X_{n}\right]} \leq \frac{1}{4}\left(\begin{array}{c} n+3 \\ 3 \end{array}\right) =\frac{1}{4} \cdot \frac{(n+3)(n+2)(n+1)}{6} \\ =\frac{n^{3}+6 n^{2}+11 n+6}{24} \end{array} 2E[Xn]41(n+33)=416(n+3)(n+2)(n+1)=24n3+6n2+11n+6

两边取对数lg便得到
E [ X n ] ≤ l g ( θ ( n 3 ) ) = 3 l g ( θ ( n ) ) = θ ( l g n ) E[X_n] \leq lg(\theta(n^3))=3lg(\theta(n))=\theta(lgn) E[Xn]lg(θ(n3))=3lg(θ(n))=θ(lgn)
即随机构建的二叉搜索树的平均高度不会高于lgn,结合基本操作耗时O(h)的结论,最终能够获得O(lgn)这个比较好的平均操作性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值