1、算法与数据结构习题答案第一章 数据结构基础1.1 简述下列概念:数据、数据元素、数据类型、数据结构、逻辑结构、存储结构、线性结构、非线性结构。 数据:指能够被计算机识别、存储和加工处理的信息载体。 数据元素:就是数据的基本单位,在某些情况下,数据元素也称为元素、结点、顶点、记录。数据元素有时可以由若干数据项组成。 数据类型:是一个值的集合以及在这些值上定义的一组操作的总称。通常数据类型可以看作是程序设计语言中已实现的数据结构。 数据结构:指的是数据之间的相互关系,即数据的组织形式。一般包括三个方面的内容:数据的逻辑结构、存储结构和数据的运算。 逻辑结构:指数据元素之间的逻辑关系。 存储结构:数
2、据元素及其关系在计算机存储器内的表示,称为数据的存储结构. 线性结构:数据逻辑结构中的一类。它的特征是若结构为非空集,则该结构有且只有一个开始结点和一个终端结点,并且所有结点都有且只有一个直接前趋和一个直接后继。线性表就是一个典型的线性结构。栈、队列、串等都是线性结构。 非线性结构:数据逻辑结构中的另一大类,它的逻辑特征是一个结点可能有多个直接前趋和直接后继。数组、广义表、树和图等数据结构都是非线性结构。.2 试举一个数据结构的例子、叙述其逻辑结构、存储结构、运算三个方面的内容。答: 例如有一张学生体检情况登记表,记录了一个班的学生的身高、体重等各项体检信息。这张登记表中,每个学生的各项体检信
3、息排在一行上。这个表就是一个数据结构。每个记录(有姓名,学号,身高和体重等字段)就是一个结点,对于整个表来说,只有一个开始结点(它的前面无记录)和一个终端结点(它的后面无记录),其他的结点则各有一个也只有一个直接前趋和直接后继(它的前面和后面均有且只有一个记录)。这几个关系就确定了这个表的逻辑结构是线性结构。这个表中的数据如何存储到计算机里,并且如何表示数据元素之间的关系呢? 即用一片连续的内存单元来存放这些记录(如用数组表示)还是随机存放各结点数据再用指针进行链接呢? 这就是存储结构的问题。在这个表的某种存储结构基础上,可实现对这张表中的记录进行查询,修改,删除等操作。对这个表可以进行哪些操
4、作以及如何实现这些操作就是数据的运算问题了。1.3 常用的存储表示方法有哪几种?答:常用的存储表示方法有四种: 顺序存储方法:它是把逻辑上相邻的结点存储在物理位置相邻的存储单元里,结点间的逻辑关系由存储单元的邻接关系来体现。由此得到的存储表示称为顺序存储结构,通常借助程序语言的数组描述。 链接存储方法:它不要求逻辑上相邻的结点在物理位置上亦相邻,结点间的逻辑关系是由附加的指针字段表示。由此得到的存储表示称为链式存储结构,通常借助于程序语言的指针类型描述。 索引存储方法:除建立存储结点信息外,还建立附加的索引表来标识结点的地址。组成索引表的索引项由结点的关键字和地址组成。若每个结点在索引表中都有
5、一个索引项,则该索引表称之为稠密索引(Dense Index)。若一组结点在索引表中只对应一个索引项,则该索引表称为稀疏索引。 散列存储方法:就是根据结点的关键字直接计算出该结点的存储地址。1.4 设三个函数f,g,h分别为 f(n)=100n3+n2+1000 , g(n)=25n3+5000n2 , h(n)=n1.5+5000nlgn 请判断下列关系是否成立: (1) f(n)=O(g(n) (2) g(n)=O(f(n) (3) h(n)=O(n1.5) (4) h(n)=O(nlgn)分析:数学符号O的严格的数学定义:若T(n)和f(n)是定义在正整数集合上的两个函数,则T(n)=O
6、(f(n)表示存在正的常数C和n0,使得当nn0时都满足0T(n)Cf(n)。通俗地说,就是当n时,f(n)的函数值增长速度与T(n)的增长速度同阶。一般,一个函数的增长速度与该函数的最高次阶同阶。即:O(f(n)=n3O(g(n)=n3O(h(n)=n1.5所以答案为:答:(1)成立。(2)成立。(3)成立。 (4)不成立。1.5 设有两个算法在同一机器上运行,其执行时间分别为100n2和2n,要使前者快于后者,n至少要多大?分析:要使前者快于后者,即前者的时间消耗低于后者,即:100n22n 求解上式,可得答:n=151.6 设n为正整数,利用大O记号,将下列程序段的执行时间表示为n的函数
7、。(1) i=1; k=0;while(in) k=k+10*i;i+;分析:i=1; /1k=0; /1 while(in) /n k=k+10*i; /n-1i+; /n-1由以上列出的各语句的频度,可得该程序段的时间消耗:T(n)=1+1+n+(n-1)+(n-1)=3n可表示为T(n)=O(n)(2) i=0; k=0;dok=k+10*i; i+;while(in);分析:i=0; /1k=0; /1do /nk=k+10*i; /ni+; /nwhile(in);/n由以上列出的各语句的频度,可得该程序段的时间消耗:T(n)=1+1+n+n+n+n=4n+2可表示为T(n)=O(n
8、)(3) i=1; j=0;while(i+jj) j+;else i+;分析:通过分析以上程序段,可将i+j看成一个控制循环次数的变量,且每执行一次循环,i+j的值加1。该程序段的主要时间消耗是while循环,而while循环共做了n次,所以该程序段的执行时间为:T(n)=O(n)(4)x=n; / n1while (x=(y+1)*(y+1)y+;分析:由x=n且x的值在程序中不变,又while的循环条件(x=(y+1)*(y+1)可知:当(y+1)*(y+1)刚超过n的值时退出循环。由(y+1)*(y+1)n得:y0)if(x100)x=x-10;y-;else x+;分析:x=91;
9、/1y=100; /1while(y0) /1101if(x100) /1100x=x-10; /100y-; /100elsex+; /1000以上程序段右侧列出了执行次数。该程序段的执行时间为:T(n)=O(1)1.7 算法的时间复杂度仅与问题的规模相关吗?答:算法的时间复杂度不仅与问题的规模相关,还与输入实例中的初始状态有关。但在最坏的情况下,其时间复杂度就是只与求解问题的规模相关的。我们在讨论时间复杂度时,一般就是以最坏情况下的时间复杂度为准的。1.8 按增长率由小至大的顺序排列下列各函数:2100, (3/2)n,(2/3)n, nn ,n0.5 , n! ,2n ,lgn ,nlg
10、n, n(3/2)答:常见的时间复杂度按数量级递增排列,依次为:常数阶0(1)、对数阶0(log2n)、线性阶0(n)、线性对数阶0(nlog2n)、平方阶0(n2)、立方阶0(n3)、k次方阶0(nk)、指数阶0(2n)。先将题中的函数分成如下几类: 常数阶:2100 对数阶:lgn K次方阶:n0.5、n(3/2)指数阶 (按指数由小到大排):nlgn、(3/2)n、2n、 n!、 nn注意:(2/3)n由于底数小于1,所以是一个递减函数,其数量级应小于常数阶。根据以上分析按增长率由小至大的顺序可排列如下:(2/3)n 2100 lgn n0.5 n(3/2) nlgn (3/2)n 2n
11、 n! next-next 和 rear, 查找时间都是O(1)。若用头指针来表示该链表,则查找终端结点的时间为O(n)。2.5 在单链表、双链表和单循环链表中,若仅知道指针p指向某结点,不知道头指针,能否将结点*p从相应的链表中删去?若可以,其时间复杂度各为多少?答:下面分别讨论三种链表的情况。1. 单链表。若指针p指向某结点时,能够根据该指针找到其直接后继,能够顺后继指针链找到*p结点后的结点。但是由于不知道其头指针,所以无法访问到p指针指向的结点的直接前趋。因此无法删去该结点。2. 双链表。由于这样的链表提供双向指针,根据*p结点的前趋指针和后继指针可以查找到其直接前趋和直接后继,从而可
12、以删除该结点。其时间复杂度为O(1)。3. 单循环链表。根据已知结点位置,可以直接得到其后相邻的结点位置(直接后继),又因为是循环链表,所以我们可以通过查找,得到p结点的直接前趋。因此可以删去p所指结点。其时间复杂度应为O(n)。2.6 下述算法的功能是什么?LinkList Demo(LinkList L) / L 是无头结点单链表ListNode *Q,*P;if(L&L-next)Q=L;L=L-next;P=L;while (P-next) P=P-next;P-next=Q; Q-next=NULL;return L;/ Demo答:该算法的功能是:将开始结点摘下链接到终端结点之后成
13、为新的终端结点,而原来的第二个结点成为新的开始结点,返回新链表的头指针。2.7 设线性表的n个结点定义为(a0,a1,.an-1),重写顺序表上实现的插入和删除算法:InsertList 和DeleteList.解:算法如下:#define ListSize 100 / 假定表空间大小为100typedef int DataType;/假定DataType的类型为int型typedef struct DataType dataListSize;/ 向量data用于存放表结点 int length; / 当前的表长度 Seqlist; /以上为定义表结构void InsertList ( Seq
14、list *L, Datatype x, int i) /将新结点x插入L所指的顺序表的第i个结点ai的位置上,即插入的合法位置为:0=ilengthint j;if ( i L - length ) Error(position error);/ 非法位置,退出,该函数定义见教材P7.if ( L-length=ListSize ) Error(“overflow);for ( j=L-length-1 ; j = i ; j -) L-data j+1=L-data j ;L-data i =x ;L-length+ ;void DeleteList ( Seqlist *L, int i
15、 )/ 从L所指的顺序表中删除第i个结点ai,合法的删除位置为0=ilength-1int j;if ( i= L- length) Error( position error ) ;for ( j = i ; j length ; j+ ) L-data j =L-data j+1; /结点前移L- length- ; /表长减小2.8 试分别用顺序表和单链表作为存储结构,实现将线性表(a0,a1,.an-1)就地逆置的操作,所谓就地指辅助空间应为O(1)。答:1. 顺序表:要将该表逆置,可以将表中的开始结点与终端结点互换,第二个结点与倒数第二个结点互换,如此反复,就可将整个表逆置了。算法如
16、下:/ 顺序表结构定义同上题void ReverseList( Seqlist *L)DataType temp ; /设置临时空间用于存放dataint i;for (i=0;ilength/2;i+)/L-length/2为整除运算 temp = L-datai; /交换数据L - data i = L - data L - length-1-i;L - data L - length - 1 - i = temp;2. 链表:分析:可以用交换数据的方式来达到逆置的目的。但是由于是单链表,数据的存取不是随机的,因此算法效率太低。可以利用指针改指来达到表逆置的目的。具体情况入下:(1)当链表
17、为空表或只有一个结点时,该链表的逆置链表与原表相同。(2)当链表含2个以上结点时,可将该链表处理成只含第一结点的带头结点链表和一个无头结点的包含该链表剩余结点的链表。然后,将该无头结点链表中的所有结点顺着链表指针,由前往后将每个结点依次从无头结点链表中摘下,作为第一个结点插入到带头结点链表中。这样就可以得到逆置的链表。算法是这样的:结点结构定义如下:typedef char DataType; /假设结点的数据域类型的字符typedef struct node /结点类型定义DataType data; /结点的数据域struct node *next;/结点的指针域ListNode;type
18、def ListNode *LinkList;ListNode *p;LinkList head;LinkList ReverseList( LinkList head )/ 将head 所指的单链表(带头结点)逆置ListNode *p ,*q ;/设置两个临时指针变量if( head-next & head-next-next) /当链表不是空表或单结点时p=head-next;q=p-next;p - next=NULL; /将开始结点变成终端结点 while (q) /每次循环将后一个结点变成开始结点p=q;q=q-next ;p-next = head- next ;head-nex
19、t = p;return head;return head; /如是空表或单结点表,直接返回head2.9 设顺序表L是一个递增有序表,试写一算法,将x插入L中,并使L仍是一个有序表。答:因已知顺序表L是递增有序表,所以只要从顺序表终端结点(设为i位置元素)开始向前寻找到第一个小于或等于x的元素位置i后插入该位置即可。在寻找过程中,由于大于x的元素都应放在x之后,所以可边寻找,边后移元素,当找到第一个小于或等于x的元素位置i时,该位置也空出来了。算法如下:/顺序表存储结构如题2.7void InsertIncreaseList( Seqlist *L , Datatype x )int i;i
20、f ( L-length=ListSize)Error(“overflow);for ( i=L - length ; i0 & L-data i-1 x ; i-)L-data i =L-data i ; / 比较并移动元素L-data i =x;L - length+;2.10 设顺序表L是一个递减有序表,试写一算法,将x插入其后仍保持L的有序性。答:与上题相类似,只要从终端结点开始往前找到第一个比x大(或相等)的结点数据,在这个位置插入就可以了。(边寻找,边移动)算法如下:void InsertDecreaseList( Seqlist *L, Datatype x )int i;if
21、( L-length=ListSize)Error(“overflow);for ( i=L - length ; i0 & L-data i-1 data i =L-data i ; / 比较并移动元素L-data i =x;L - length+;2.11 写一算法在单链表上实现线性表的ListLength(L)运算。解:由于在单链表中只给出一个头指针,所以只能用遍历的方法来数单链表中的结点个数了。算法如下:int ListLength ( LinkList L )int len=0 ;ListNode *p;p=L; /设该表有头结点while ( p-next )p=p-next;le
22、n+;return len;2.12 已知L1和L2分别指向两个单链表的头结点,且已知其长度分别为m和n。试写一算法将这两个链表连接在一起,请分析你的算法的时间复杂度。解:分析:由于要进行的是两单链表的连接,所以应找到放在前面的那张表的表尾结点,再将后表的开始结点链接到前表的终端结点后即可。该算法的主要时间消耗是用在寻找第一张表的终端尾结点上。这两张单链表的连接顺序无要求,并且已知两表的表长,则为了提高算法效率,可选表长小的单链表在前的方式连接。具体算法如下:LinkList Link( LinkList L1 , LinkList L2,int m,int n )/将两个单链表连接在一起Li
23、stNode *p , *q, *s ;/s指向短表的头结点,q指向长表的开始结点,回收长表头结点空间if (mnext;free(L2);else s=L2;q=L1-next;free(L1);p=s;while ( p-next ) p=p-next; /查找短表终端结点p-next = q; /将长表的开始结点链接在短表终端结点后return s;本算法的主要操作时间花费在查找短表的终端结点上,所以本算的法时间复杂度为:O(min(m,n)2.13 设 A和B是两个单链表,其表中元素递增有序。试写一算法将A和B归并成一个按元素值递减有序的单链表C,并要求辅助空间为O(1),请分析算法的时间复杂度。解:根据已知条件,A和B是两个递增有序表,所以可以先取A表的表头建立空的C表。然后同时扫描A表和B表,将两表中最大的结点从对应表中摘下,并作为开始结点插入C表中。如此反复,直到A表或B表为空。最后将不为空的A表或B表中的结点依次摘下并作为开始结点插入C表中。这时,得到的C表就是由A表和B表归并成的一个按元素值递减有序的单链表C。并且辅助空间为O(1)。算法如下:LinkList MergeSort ( LinkList A , LinkList B )/ 归并两个带头结点的递增有序表为一个带头结点递减有序表Li
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1