数据结构第7章.docx

上传人:b****7 文档编号:10582010 上传时间:2023-02-21 格式:DOCX 页数:38 大小:101.79KB
下载 相关 举报
数据结构第7章.docx_第1页
第1页 / 共38页
数据结构第7章.docx_第2页
第2页 / 共38页
数据结构第7章.docx_第3页
第3页 / 共38页
数据结构第7章.docx_第4页
第4页 / 共38页
数据结构第7章.docx_第5页
第5页 / 共38页
点击查看更多>>
下载资源
资源描述

数据结构第7章.docx

《数据结构第7章.docx》由会员分享,可在线阅读,更多相关《数据结构第7章.docx(38页珍藏版)》请在冰豆网上搜索。

数据结构第7章.docx

数据结构第7章

第7章搜索

一、复习要点

本章讨论了搜索方法和简单的性能分析方法,包括适用于静态搜索表的顺序搜索和折半搜索及代表动态搜索表的二叉搜索树和AVL树。

可以使用扩充的二叉搜索树描述顺序搜索和折半搜索,从而推导出估算搜索效率的公式。

静态搜索表在整个程序的运行期间结构不会变化,其搜索效率随着表中对象的个数n不断增长。

动态搜索表因各个对象的输入顺序不同,得到的搜索表的形态不同,典型的是二叉搜索树。

在具有n个对象的二叉搜索树中,搜索效率最高的是高度最低的二叉搜索树。

为确保二叉搜索树始终保持搜索效率最高,必须在输入新的对象时判断二叉搜索树是否“失去平衡”,并进行适当的平衡旋转,使二叉搜索树的高度降到最低。

这就是AVL树。

在AVL树的讨论中,4种平衡旋转,选择参加平衡旋转的3个结点是关键,必须加以注意。

本章复习的要点是:

1、基本知识点

理解理解搜索的概念,理解静态搜索表结构,掌握静态搜索表的顺序搜索和折半搜索算法及其性能分析方法。

掌握二叉搜索树的表示、搜索、插入、删除算法及其性能分析方法,掌握AVL树的构造、插入、删除时的调整方法及其性能分析,重点是AVL树的定义、平衡化旋转、AVL树的插入和删除、AVL树的高度。

2、算法设计

用有序链表表示集合时的求集合的并、交、差的算法

并查集中的构造函数、求根及合并算法

并查集中根据树的高度和根据树中结点个数进行合并的算法

设置监视哨的顺序搜索算法和不设监视哨的顺序搜索算法

有序顺序表的顺序搜索算法

有序顺序表的折半搜索的递归算法和非递归算法

二叉搜索树的搜索、插入和删除算法

计算AVL树中指定结点高度的递归算法及利用此算法计算结点平衡因子的算法

二、难点和重点

3、基本搜索方法

Ø对一般表的顺序搜索算法(包括有监视哨和没有监视哨)

Ø对有序顺序表的顺序搜索算法,包括递归和非递归算法

Ø用判定树(即扩充二叉搜索树)描述有序顺序表的顺序搜索,以及平均搜索长度(成功与不成功)的计算。

Ø对有序顺序表的折半搜索算法、包括递归和非递归算法

Ø用判定树(即扩充二叉搜索树)描述有序顺序表的折半搜索,以及平均搜索长度(成功与不成功)的计算。

4、二叉搜索树

Ø动态搜索树与静态搜索树的特性

Ø二叉搜索树的定义、二叉搜索树上的递归和非递归搜索算法

Ø二叉搜索树搜索时的平均搜索长度(成功与不成功)的计算

Ø二叉搜索树的插入与删除算法

ØAVL树结点上的平衡因子、AVL树的平衡旋转方法

Ø高度为h的AVL树上的最少结点个数与最多结点个数

ØAVL树的搜索方法、插入与删除方法(不要求算法)

三、教材中习题的解析

7-8设有序顺序表中的元素依次为017,094,154,170,275,503,509,512,553,612,677,765,897,908。

试画出对其进行折半搜索时的二叉搜索树,并计算搜索成功的平均搜索长度和搜索不成功的平均搜索长度。

【解答】

908

7-9若对有n个元素的有序顺序表和无序顺序表进行顺序搜索,试就下列三种情况分别讨论两者在等搜索概率时的平均搜索长度是否相同?

(1)搜索失败;

(2)搜索成功,且表中只有一个关键码等于给定值k的对象;

(3)搜索成功,且表中有若干个关键码等于给定值k的对象,要求一次搜索找出所有对象。

【解答】

(1)不同。

因为有序顺序表搜索到其关键码比要查找值大的对象时就停止搜索,报告失败信息,不必搜索到表尾;而无序顺序表必须搜索到表尾才能断定搜索失败。

(2)相同。

搜索到表中对象的关键码等于给定值时就停止搜索,报告成功信息。

(3)不同。

有序顺序表中关键码相等的对象相继排列在一起,只要搜索到第一个就可以连续搜索到其它关键码相同的对象。

而无序顺序表必须搜索全部表中对象才能确定相同关键码的对象都找了出来,所需时间就不相同了。

前两问可做定量分析。

第三问推导出的公式比较复杂,不再进一步讨论。

7-10假定用一个循环链表来实现一个有序表,并让指针head指向具有最小关键码的结点。

指针current初始时等于head,每次搜索后指向当前检索的结点,但如果搜索不成功则current重置为head。

试编写一个函数search(head,current,key)实现这种搜索。

当搜索成功时函数返回被检索的结点地址,若搜索不成功则函数返回空指针0。

请说明如何保持指针current以减少搜索时的平均搜索长度。

【解答】

current

60

50

40

30

20

10

head

相应的搜索函数可以定义为链表及链表结点类的友元函数,直接使用链表及链表结点类的私有数据成员。

template

ListNode*Search(ListNode*head,ListNode*¤t,Typekey){

ListNode*p,*q;

if(key

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的结点,它只有一个或零

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 哲学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1