算法导论:第一部分
第一部分:基础知识
12 二叉搜索树
12.1 二叉搜索树
利用比较排序,建立的二叉搜索树,树的根是元素的中值(偶数时取小者),使得树的高度取最小。程序如下:
//#include"buildBinarySearchTree.h"
#include<iostream>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std;
int Left(int i){
return 2*i+1;
}
int Right(int i){
return 2*i+2;
}
int Parent(int i){
return (i-1)/2;
}
void buildBinarySearchTree
(vector<int>& input,vector<int>& output,int left,int right,int pos){
if (left==right){
int middle=(left+right)/2;
output[pos]=input[middle];
return;
}else{
int middle=(left+right)/2;
output[pos]=input[middle];
if (left<=middle-1)
buildBinarySearchTree(input,output,left,
middle-1,Left(pos));
if (right>=middle+1)
buildBinarySearchTree(input,output,
middle+1,right,Right(pos));
return;
}
}
vector<int> BuildTree(vector<int>& input){
int highth=log(input.size())/log(2)-1;
int size=pow(2,highth+2)-1;
vector<int> output(size,-9999);
sort(input.begin(),input.end());
buildBinarySearchTree(input,output,0,input.size()-1,0);
return output;
}
void InorderTreeWalk(vector<int>& A,int x=0){
if (x<A.size()&&A[x]!=-9999){
InorderTreeWalk(A,Left(x));
cout<<A[x]<<" ";
InorderTreeWalk(A,Right(x));
}
}
#include"buildBinarySearchTree.h"
int main(int argc,char** argv){
vector<int> A={2,1,3,4,123,123,5,7,98,123123};
auto B=BuildTree(A);
InorderTreeWalk(B);
}
练习12.1-1
高度为2时:
其他同理可以得到。
练习 12.1-2
二叉搜索树的父节点的关键字是其子树的中间的值(或者等于),而最小堆的父节点的关键字比其子树的关键字都要小(或者等于)。最小堆是不能在 O ( n ) O(n) O(n)输出一棵 n n n个结点树的关键字的。这是因为,如果每次取父节点的值达到从小到大输出的话,每次输出必须要维护一次最小堆的性质,所以其时间复杂度为 Θ ( n l g n ) \Theta(nlgn) Θ(nlgn)。
练习12.1-3
#include"buildBinarySearchTree.h"
void InorderTreeWalk2(vector<int>& A,int AvalidNumber){
vector<int> print;
int x=0,cnt=0;
while(cnt<AvalidNumber){
if(x<A.size()&&A[x]!=-9999){
print.push_back(x);
x=Left(x);
}else{
cout<<A[print[print.size()-1]]<<" ";
cnt++;
print.pop_back();
x=Right(Parent(x));
if(cnt>=AvalidNumber)
break;
if(x<A.size()&&A[x]!=-9999){
print.push_back(x);
x=Left(x);
}else{
x=print[print.size()-1];
cout<<A[print[print.size()-1]]<<" ";
cnt++;
print.pop_back();
x=Right(x);
}
}
}
cout<<endl;
}
int main(int argc,char** argv){
vector<int> A={2,1,3,4,123,5,7,98,123123};
auto Asize=A.size();
auto B=BuildTree(A);
InorderTreeWalk2(B,Asize);
}
练习12.1-4
#include"buildBinarySearchTree.h"
void PreorderTreeWalk(vector<int>& A,int x=0){
if (x<A.size()&&A[x]!=-9999){
cout<<A[x]<<" ";
PreorderTreeWalk(A,Left(x));
PreorderTreeWalk(A,Right(x));
}
}
void PostorderTreeWalk(vector<int>& A,int x=0){
if (x<A.size()&&A[x]!=-9999){
PostorderTreeWalk(A,Left(x));
PostorderTreeWalk(A,Right(x));
cout<<A[x]<<" ";
}
}
int main(int argc,char** argv){
vector<int> A={2,1,3,4,123,5,7,98,123123};
auto B=BuildTree(A);
PreorderTreeWalk(B);
cout<<endl;
PostorderTreeWalk(B);
cout<<endl;
}
练习12.1-5
前面的建立树的过程就能知道,排序数组需要 Θ ( n l g n ) \Theta(nlgn) Θ(nlgn),建立二叉搜索树只需要 Θ ( n ) \Theta(n) Θ(n),所以最坏情况下的时间复杂度为 Θ ( n l g n ) \Theta(nlgn) Θ(nlgn)。
12.2 查询二叉搜索树
二叉树的 s e a r c h ( ) search() search(); m a x i m u m ( ) maximum() maximum(), m i n i m u m ( ) minimum() minimum(), s u c c e s s o r ( ) successor() successor(), p r e d e c e s s o r ( ) predecessor() predecessor(),函数如下:
#include"buildBinarySearchTree.h"
int TreeSearch(vector<int>A, int k,int x=0){
while(x!=-9999&&x<A.size()&&k!=A[x]){
if(k<A[x])
x=Left(x);
else
x=Right(x);
}
return x;
}
int TreeMinimum(vector<int> A,int x=0){
while(x<A.size()&&A[x]!=-9999)
x=Left(x);
return Parent(x);
}
int TreeMaximum(vector<int> A,int x=0){
while(x<A.size()&&A[x]!=-9999)
x=Right(x);
return Parent(x);
}
int TreeSuccssor(vector<int>A,int x){
if (Right(x)<A.size()&&Right(x)!=-9999)
return TreeMinimum(A,Right(x));
int y=Parent(x);
while(y<A.size()&&y!=-9999&&x==Right(y)){
x=y;
y=Parent(y);
}
return y;
}
int TreePredecessor(vector<int>A,int x){
if (Left(x)<A.size()&&Left(x)!=-9999)
return TreeMaximum(A,Left(x));
int y=Parent(x);
while(y<A.size()&&y!=-9999&&x==Left(y)){
x=y;
y=Parent(y);
}
return y;
}
int main(int argc,char** argv){
vector<int> A={2,1,3,4,123,123,5,7,98,123123};
auto Asize=A.size();
auto B=BuildTree(A);
InorderTreeWalk(B);
cout<<endl;
cout<<B[TreeSearch(B,5)]<<endl;
cout<<B[TreeMinimum(B)]<<endl;
cout<<B[TreeMaximum(B)]<<endl;
cout<<B[TreeSuccssor(B,TreeSearch(B,5))]<<endl;
cout<<B[TreePredecessor(B,TreeSearch(B,5))]<<endl;
}
练习12.2-1
c
c
c和
d
d
d是错的。
在
c
c
c中:
240
240
240是
911
911
911的左孩,
912
912
912是
240
240
240的右孩,而
912
>
911
912>911
912>911确在
911
911
911的左孩边,违反二叉搜索树性质;
在
d
d
d中:类似上面的原因,
299
<
347
299<347
299<347确在
347
347
347的右孩边。
练习12.2-2
简单,略。
练习12.2-3
见前面,略。
练习12.2-4
简单,略。比如: 1 , 4 , 3 , 2 , 2.1 1,4,3,2,2.1 1,4,3,2,2.1。查找路径为 1 , 4 , 3 , 2 1,4,3,2 1,4,3,2。显然 2.1 2.1 2.1属于 C C C, 4 4 4属于 B B B, 2.1 < 4 2.1<4 2.1<4。
练习12.2-5
反证就出来了,如果一个结点 a a a的后继 b b b有左孩 c c c,显然, a < = c < = b a<=c<=b a<=c<=b这与 b b b是 a a a的后继相矛盾。所以结点的后继没有左孩。前驱没右孩同理。
练习12.2-6
准确地说,如果一个结点
x
x
x的子树无右孩,它的后继是第一个视
x
x
x为左子树中结点的先祖(不大理解书中"最低层先祖"是否指这个,还是指根结点)。
证明很明显,如果
x
x
x无右孩,则它是一棵视它为右子树的树的最大值,那么这颗树的根结点
y
y
y的双亲
y
.
p
y.p
y.p和双亲的右树结点大于
x
x
x,而由搜索二叉树的性质知道,结点
y
.
p
y.p
y.p的值是比其右树的所有结点值要小,所以
x
x
x的后继是
y
.
p
y.p
y.p,而且由上面推述可知,
y
=
y
.
p
.
l
e
f
t
y=y.p.left
y=y.p.left也是
x
x
x的祖先。
练习12.2-7
显然,在一棵以
x
0
x_0
x0为根结点的搜索二叉树中,其往下搜索(指
x
=
x
.
l
e
f
t
x=x.left
x=x.left或者
x
=
x
.
r
i
g
h
t
x=x.right
x=x.right)的次数
T
d
o
w
n
T_{down}
Tdown是大于往上搜索(指
x
=
x
.
p
x=x.p
x=x.p)的次数
T
u
p
T_{up}
Tup的。所以此算法的时间复杂度
T
T
T有:
T
d
o
w
n
≤
T
≤
2
T
d
o
w
n
T_{down}\leq T\leq 2T_{down}
Tdown≤T≤2Tdown
又由于往下搜索历遍所有元素所以
T
d
o
w
n
=
n
−
1
T_{down}=n-1
Tdown=n−1,所以时间复杂度
T
=
Θ
(
n
)
T=\Theta (n)
T=Θ(n)。