else{p=current;q=head;}
while(p!
=q&&p->datalink;//循链搜索其值等于key的结点
if(p->data==key){current=p;returnp;}//找到,返回结点地址
else{current=head;returnNULL;}//未找到,返回空指针
}
7-11考虑用双向链表来实现一个有序表,使得能在这个表中进行正向和反向搜索。
若指针p总是指向最后成功搜索到的结点,搜索可以从p指示的结点出发沿任一方向进行。
试根据这种情况编写一个函数search(head,p,key),检索具有关键码key的结点,并相应地修改p。
最后请给出搜索成功和搜索不成功时的平均搜索长度。
p
【解答】
40
70
∧
∧
60
50
30
20
10
head
template
DblListNode*Search(DblListNode*head,DblListNode*&p,Typekey){
//在以head为表头的双向有序链表中搜索具有值key的结点。
算法可视为双向链表类和双向链表
//结点类的友元函数。
若给定值key大于结点p中的数据,从p向右正向搜索,否则,从p向左反
//向搜索。
DblListNode*q=p;
if(keydata){while(q!
=NULL&&q->data>key)q=q->lLink;}//反向搜索
else{while(q!
=NULL&&q->datarLink;}//正向搜索
if(q!
=NULL&&q->data==key){p=q;returnp;}//搜索成功
elsereturnNULL;
}
如果指针p处于第i个结点(i=1,2,…,n),它左边有i-1个结点,右边有n-i个结点。
找到左边第i-1号结点比较2次,找到第i-2号结点比较3次,…,找到第1号结点比较i次,一般地,找到左边第k个结点比较i-k+1次(k=1,2,…,i-1)。
找到右边第i+1号结点比较2次,找到第i+2号结点比较3次,…,找到第n号结点比较n-i+1次,一般地,找到右边第k个结点比较k-i+1次(k=i+1,i+2,…,n)。
因此,当指针处于第i个结点时的搜索成功的平均数据比较次数为
一般地,搜索成功的平均数据比较次数为
如果指针p处于第i个结点(i=1,2,…,n),它左边有i个不成功的位置,右边有n-i+1个不成功的位置。
一般地,搜索不成功的平均数据比较次数为
7-12在一棵表示有序集S的二叉搜索树中,任意一条从根到叶结点的路径将S分为3部分:
在该路径左边结点中的元素组成的集合S1;在该路径上的结点中的元素组成的集合S2;在该路径右边结点中的元素组成的集合S3。
S=S1S2S3。
若对于任意的aS1,bS2,cS3,是否总有abc?
为什么?
【解答】
答案是否定的。
举个反例:
看下图粗线所示的路径
15
35
50
65
70
45
40
30
25
S1={15},S2={25,30,35,45},S3={40,50,65,70}
c=40S3,b=45S2,bc不成立。
7-13请给出下列操作序列运算的结果:
Union(1,2),Union(3,4),Union(3,5),Union(1,7),Union(3,6),Union(8,9),Union(1,8),Union(3,10),Union(3,11),Union(3,12),Union(3,13),Union(14,15),Union(16,17),Union(14,16),Union(1,3),Union(1,14),要求
(1)以任意方式执行Union;
(2)根据树的高度执行Union;
(3)根据树中结点个数执行Union。
【解答】
(1)对于union(i,j),以i作为j的双亲
1
2
15
3
10
9
8
7
0
16
14
13
11
12
6
5
4
7
(2)按i和j为根的树的高度实现union(i,j),高度大者为高度小者的双亲;
8
1
2
14
3
10
6
5
4
9
16
15
13
11
12
0
(3)按i和j为根的树的结点个数实现union(i,j),结点个数大者为结点个数小者的双亲。
14
0
16
15
1
8
7
2
9
3
4
5
6
10
11
13
12
7-14有n个结点的二叉搜索树具有多少种不同形态?
【解答】
7-15设有一个输入数据的序列是{46,25,78,62,12,37,70,29},试画出从空树起,逐个输入各个数据而生成的二叉搜索树。
【解答】
46
46
46
46
46
46
78
25
25
78
25
78
25
78
25
加25
加46
空树
加78
37
12
62
12
62
62
加37
加12
加62
46
46
78
25
78
25
12
62
37
12
62
37
加29
加70
29
70
70
7-16设有一个标识符序列{else,public,return,template},p1=0.05,p2=0.2,p3=0.1,p4=0.05,q0=0.2,q1=0.1,q2=0.2,q3=0.05,q4=0.05,计算W[i][j]、C[i][j]和R[i][j],构造最优二叉搜索树。
【解答】
将标识符序列简化为{e,p,r,t},并将各个搜索概率值化整,有
e
p
r
t
p1=1
p2=4
p3=2
p4=1
q0=4
q1=2
q2=4
q3=1
q4=1
(1)首先构造只有一个内结点的最优二叉搜索树:
p1
p2
p4
p3
q4
q3
q3
q2
q2
q0
q1
q1
三个矩阵的内容如下:
0
1
2
3
4
0
1
2
3
4
0
1
2
3
4
0
4
7
15
18
20
0
0
7
22
32
39
0
0
1
2
2
2
1
2
10
13
15
1
0
10
20
27
1
0
2
2
2
2
4
7
9
2
0
7
12
2
0
3
3
3
1
3
3
0
3
3
0
4
4
1
4
0
4
0
W[i][j]C[i][j]R[i][j]
(2)构造具有两个内结点的最优二叉搜索树
C[2][4]=12
T[2][4]=2
C[1][3]=20
T[1][3]=2
C[0][2]=22
T[0][2]=2
p3
p2
p2
q2
p4
p3
p1
q2
q1
q4
q3
q3
q2
q1
q0
(3)构造具有三个内结点的最优二叉搜索树
C[0][3]=32
T[0][3]=2
C[1][4]=27
T[1][4]=2
p2
p2
p1
p3
q1
p3
p4
q2
q3
q2
q1
q0
q4
q3
(4)构造具有四个内结点的最优二叉搜索树
C[0][4]=39
T[0][4]=2
p2
左子树T[0][1],其C[0][1]=7
右子树T[2][4],其C[2][4]=12
p1
p3
q2
q1
q0
p4
q4
q3
7-17在二叉搜索树上删除一个有两个子女的结点时,可以采用以下三种方法:
(1)用左子树TL上具有最大关键码的结点X顶替,再递归地删除X。
(2)交替地用左子树TL上具有最大关键码的结点和右子树TR上具有最小关键码的结点顶替,再递归地删除适当的结点。
(3)用左子树TL上具有最大关键码的结点或者用右子树TR上具有最小关键码的结点顶替,再递归地删除适当的结点。
可随机选择其中一个方案。
试编写程序实现这三个删除方法,并用实例说明哪一个方法最易于达到平衡化。
【解答】
1在被删结点有两个子女时用左子树TL中具最大关键码的结点顶替的算法:
templateBstNode*BST:
:
leftReplace(BstNode*ptr){
BstNode*temp=ptr->leftChild;//进到ptr的左子树
while(temp->rightChild!
=NULL)temp=temp->rightChild;//搜寻中序下最后一个结点
ptr->data=temp->data;//用该结点数据代替根结点数据
returntemp;
}
②在被删结点有两个子女时用右子树TR中具最小关键码的结点顶替的算法:
templateBstNode*BST:
:
rightReplace(BstNode*ptr){
BstNode*temp=ptr->rightChild;//进到ptr的右子树
while(temp->leftChild!
=NULL)temp=temp->leftChild;//搜寻中序下最后一个结点
ptr->data=temp->data;//用该结点数据代替根结点数据
returntemp;
}
(1)用左子树TL上具有最大关键码的结点X顶替,再递归地删除X。
templatevoidBST:
:
Remove(Type&x,BstNode*&ptr){
//私有函数:
在以ptr为根的二叉搜索树中删除含x的结点。
若删除成功则新根通过ptr返回。
BstNode*temp;
if(ptr!
=NULL)
if(xdata)Remove(x,ptr->leftChild);//在左子树中执行删除
elseif(x>ptr->data)Remove(x,ptr->rightChild);//在右子树中执行删除
elseif(ptr->leftChild!
=NULL&&ptr->rightChild!
=NULL){
//ptr指示关键码为x的结点,它有两个子女
temp=leftReplace(ptr);//在ptr的左子树中搜寻中序下最后一个结点顶替x
Remove(ptr->data,ptr->rightChild);//在ptr的右子树中删除该结点
}
else{//ptr指示关键码为x的结点,它只有一个或零个子女
temp=ptr;
if(ptr->leftChild==NULL)ptr=ptr->rightChild;//只有右子女
elseif(ptr->rightChild==NULL)ptr=ptr->leftChild;//只有左子女
deletetemp;
}
}
(2)交替地用左子树TL上具有最大关键码的结点和右子树TR上具有最小关键码的结点顶替,再递归地删除适当的结点。
templatevoidBST:
:
Remove(Type&x,BstNode*&ptr,int&dir){
//私有函数:
在以ptr为根的二叉搜索树中删除含x的结点。
若删除成功则新根通过ptr返回。
在
//参数表中有一个引用变量dir,作为调整方向的标记。
若dir=0,用左子树上具有最大关键码的
//结点顶替被删关键码;若dir=1,用右子树上具有最小关键码的结点顶替被删关键码结点,在调
//用它的程序中设定它的初始值为0。
BstNode*temp;
if(ptr!
=NULL)
if(xdata)Remove(x,ptr->leftChild,dir);//在左子树中执行删除
elseif(x>ptr->data)Remove(x,ptr->rightChild,dir);//在右子树中执行删除
elseif(ptr->leftChild!
=NULL&&ptr->rightChild!
=NULL){
//ptr指示关键码为x的结点,它有两个子女
if(dir==0){
temp=leftReplace(ptr);dir=1;
//在ptr的左子树中搜寻中序下最后一个结点顶替x
Remove(ptr->data,ptr->rightChild,dir);//在ptr的右子树中删除该结点
}else{
temp=rightReplace(ptr);dir=0;
//在ptr的右子树中搜寻中序下第一个结点顶替x
Remove(ptr->data,ptr->leftChild,dir);//在ptr的左子树中删除该结点
}
}
else{//ptr指示关键码为x的结点,它只有一个或零个子女
temp=ptr;
if(ptr->leftChild==NULL)ptr=ptr->rightChild;//只有右子女
elseif(ptr->rightChild==NULL)ptr=ptr->leftChild;//只有左子女
deletetemp;
}
}
(3)用左子树TL上具有最大关键码的结点或者用右子树TR上具有最小关键码的结点顶替,再递归地删除适当的结点。
可随机选择其中一个方案。
#include
templatevoidBST:
:
Remove(Type&x,BstNode*&ptr){
//私有函数:
在以ptr为根的二叉搜索树中删除含x的结点。
若删除成功则新根通过ptr返回。
在
//程序中用到一个随机数发生器rand(),产生032767之间的随机数,将它除以16384,得到02
//之间的浮点数。
若其大于1,用左子树上具有最大关键码的结点顶替被删关键码;若其小于或等
//于1,用右子树上具有最小关键码的结点顶替被删关键码结点,在调用它的程序中设定它的初始
//值为0。
BstNode*temp;
if(ptr!
=NULL)
if(xdata)Remove(x,ptr->leftChild);//在左子树中执行删除
elseif(x>ptr->data)Remove(x,ptr->rightChild);//在右子树中执行删除
elseif(ptr->leftChild!
=NULL&&ptr->rightChild!
=NULL){
//ptr指示关键码为x的结点,它有两个子女
if((float)(rand()/16384)>1){
temp=leftReplace(ptr);//在ptr的左子树中搜寻中序下最后一个结点顶替x
Remove(ptr->data,ptr->rightChild);//在ptr的右子树中删除该结点
}else{
temp=rightReplace(ptr);//在ptr的右子树中搜寻中序下第一个结点顶替x
Remove(ptr->data,ptr->leftChild);//在ptr的左子树中删除该结点
}
}
else{//ptr指示关键码为x的结点,它只有一个或零