数据结构复习资料及答案.docx
- 文档编号:23233111
- 上传时间:2023-05-15
- 格式:DOCX
- 页数:69
- 大小:79.15KB
数据结构复习资料及答案.docx
《数据结构复习资料及答案.docx》由会员分享,可在线阅读,更多相关《数据结构复习资料及答案.docx(69页珍藏版)》请在冰豆网上搜索。
数据结构复习资料及答案
数据结构
习题一
1.1简述下列概念:
数据、数据元素、数据类型、数据结构、逻辑结构、存储结构、线性结构、非线性结构。
◆数据:
指能够被计算机识别、存储和加工处理的信息载体。
◆数据元素:
就是数据的基本单位,在某些情况下,数据元素也称为元素、结点、顶点、记录。
数据元素有时可以由若干数据项组成。
◆数据类型:
是一个值的集合以及在这些值上定义的一组操作的总称。
◆数据结构:
指的是数据之间的相互关系,即数据的组织形式。
一般包括三个方面的内容:
数据的逻辑结构、存储结构和数据的运算。
◆逻辑结构:
指各数据元素之间的逻辑关系。
◆存储结构:
就是数据的逻辑结构用计算机语言的实现。
◆线性结构:
数据逻辑结构中的一类,它的特征是若结构为非空集,则该结构有且只有一个开始结点和一个终端结点,并且所有结点都最多只有一个直接前趋和一个直接后继。
线性表就是一个典型的线性结构。
◆非线性结构:
数据逻辑结构中的另一大类,它的逻辑特征是一个结点可能有多个直接前趋和直接后继。
1.2试举一个数据结构的例子、叙述其逻辑结构、存储结构、运算三个方面的内容。
(看教材)
1.3常用的存储表示方法有哪几种?
常用的存储表示方法有四种:
◆顺序存储方法:
它是把逻辑上相邻的结点存储在物理位置相邻的存储单元里,结点间的逻辑关系由存储单元的邻接关系来体现。
由此得到的存储表示称为顺序存储结构。
◆链接存储方法:
它不要求逻辑上相邻的结点在物理位置上亦相邻,结点间的逻辑关系是由附加的指针字段表示的。
由此得到的存储表示称为链式存储结构。
◆索引存储方法:
除建立存储结点信息外,还建立附加的索引表来标识结点的地址。
◆散列存储方法:
就是根据结点的关键字直接计算出该结点的存储地址。
1.4设三个函数f,g,h分别为f(n)=100n^3+n^2+1000,g(n)=25n^3+5000n^2,h(n)=n^1.5+5000nlgn请判断下列关系是否成立:
1f(n)=O(g(n))2g(n)=O(f(n))3h(n)=O(n^1.5)(4)h(n)=O(nlgn)
◆
(1)成立。
◇这里我们复习一下渐近时间复杂度的表示法T(n)=O(f(n)),这里的"O"是数学符号,它的严格定义是"若T(n)和f(n)是定义在正整数集合上的两个函数,则T(n)=O(f(n))表示存在正的常数C和n0,使得当n≥n0时都满足0≤T(n)≤C·f(n)。
"用容易理解的话说就是这两个函数当整型自变量n趋向于无穷大时,两者的比值是一个不等于0的常数。
这么一来,就好计算了吧。
第
(1)题中两个函数的最高次项都是n^3,因此当n→∞时,两个函数的比值是一个常数,所以这个关系式是成立的。
◆
(2)成立。
◆(3)成立。
◆(4)不成立。
1.5设有两个算法在同一机器上运行,其执行时间分别为100n^2和2^n,要使前者快于后者,n至少要多大?
◆15最简单最笨的办法就是拿自然数去代呗。
假定n取为10,则前者的值是10000,后者的值是1024,小于前者,那我们就加个5,用15代入得前者为22500,后者为32768,已经比前者大但相差不多,那我们再减个1,用14代入得,前者为19600,后者为16384,又比前者小了,所以结果得出来就是n至少要是15.
1.6设n为正整数,利用大"O"记号,将下列程序段的执行时间表示为n的函数。
(1)i=1;k=0
while(i ◆T(n)=n-1∴T(n)=O(n) ◇这个函数是按线性阶递增的 (2)i=0;k=0; do{k=k+10*i;i++;} while(i ◆T(n)=n∴T(n)=O(n) ◇这也是线性阶递增的 (3)i=1;j=0; while(i+j<=n) {if(i ◆T(n)=n/2∴T(n)=O(n) ◇虽然时间函数是n/2,但其数量级仍是按线性阶递增的。 (4)x=n;//n>1 while(x>=(y+1)*(y+1)) y++; ◆T(n)=n1/2∴T(n)=O(n1/2)◇最坏的情况是y=0,那么循环的次数是n1/2次,这是一个按平方根阶递增的函数。 (5)x=91;y=100; while(y>0) if(x>100){x=x-10;y--;}elsex++; ◆T(n)=O (1)◇这个程序总共循环运行了1000次,但是我们看到n没有? 没。 这段程序的运行是和n无关的,只是一个常数阶的函数。 1.7算法的时间复杂度仅与问题的规模相关吗? ◆No,事实上,算法的时间复杂度不仅与问题的规模相关,还与输入实例中的元素取值等相关,但在最坏的情况下,其时间复杂度就是只与求解问题的规模相关的。 我们在讨论时间复杂度时,一般就是以最坏情况下的时间复杂度为准的。 1.8按增长率由小至大的顺序排列下列各函数: 2^100,(2/3)^n,(3/2)^n,n^n,,n! ,2^n,lgn,n^lgn,n^(3/2) ◇分析如下: 2^100是常数阶;(2/3)^n和 (3/2)^n是指数阶,其中前者是随n的增大而减小的;n^n是指数方阶;√n是方根阶,n! 就是n(n-1)(n-2)...就相当于n次方阶;2^n是指数阶,lgn是对数阶,n^lgn是对数方阶, n^(3/2)是3/2次方阶。 根据以上分析按增长率由小至大的顺序可排列如下: ◆(2/3)^n<2^100 (3/2)^n<2^n 1.9有时为了比较两个同数量级算法的优劣,须突出主项的常数因子,而将低次项用大"O"记号表示。 例如,设T1(n)=1.39nlgn+100n+256=1.39nlgn+O(n), T2(n)=2.0nlgn-2n=2.0lgn+O(n), 这两个式子表示,当n足够大时T1(n)优于T2(n),因为前者的常数因子小于后者。 请用此方法表示下列函数,并指出当n足够大时,哪一个较优,哪一个较劣? 函数 大"O"表示 优劣 (1)T1(n)=5n^2-3n+60lgn ◆5n^2+O(n) ◆较差 (2)T2(n)=3n^2+1000n+3lgn ◆3n^2+O(n) ◆其次 (3)T3(n)=8n^2+3lgn ◆8n^2+O(lgn) ◆最差 (4)T4(n)=1.5n^2+6000nlgn ◆1.5n^2+O(nlgn) ◆最优 第二章线性表 习题及答案 一、基础知识题 2.1试描述头指针、头结点、开始结点的区别、并说明头指针和头结点的作用。 答: 开始结点是指链表中的第一个结点,也就是没有直接前趋的那个结点。 链表的头指针是一指向链表开始结点的指针(没有头结点时),单链表由头指针唯一确定,因此单链表可以用头指针的名字来命名。 头结点是我们人为地在链表的开始结点之前附加的一个结点。 有了头结点之后,头指针指向头结点,不论链表否为空,头指针总是非空。 而且头指针的设置使得对链表的第一个位置上的操作与在表其他位置上的操作一致(都是在某一结点之后)。 2.2何时选用顺序表、何时选用链表作为线性表的存储结构为宜? 在实际应用中,应根据具体问题的要求和性质来选择顺序表或链表作为线性表的存储结构,通常有以下几方面的考虑: 1.基于空间的考虑。 当要求存储的线性表长度变化不大,易于事先确定其大小时,为了节约存储空间,宜采用顺序表;反之,当线性表长度变化大,难以估计其存储规模时,采用动态链表作为存储结构为好。 2.基于时间的考虑。 若线性表的操作主要是进行查找,很少做插入和删除操作时,采用顺序表做存储结构为宜;反之,若需要对线性表进行频繁地插入或删除等的操作时,宜采用链表做存储结构。 并且,若链表的插入和删除主要发生在表的首尾两端,则采用尾指针表示的单循环链表为宜。 2.3在顺序表中插入和删除一个结点需平均移动多少个结点? 具体的移动次数取决于哪两个因素? 在等概率情况下,顺序表中插入一个结点需平均移动n/2个结点。 删除一个结点需平均移动(n-1)/2个结点。 具体的移动次数取决于顺序表的长度n以及需插入或删除的位置i。 i越接近n则所需移动的结点数越少。 2.4为什么在单循环链表中设置尾指针比设置头指针更好? 尾指针是指向终端结点的指针,用它来表示单循环链表可以使得查找链表的开始结点和终端结点都很方便,设一带头结点的单循环链表,其尾指针为rear,则开始结点和终端结点的位置分别是rear->next->next和rear,查找时间都是O (1)。 若用头指针来表示该链表,则查找终端结点的时间为O(n)。 2.5在单链表、双链表和单循环链表中,若仅知道指针p指向某结点,不知道头指针,能否将结点*p从相应的链表中删去? 若可以,其时间复杂度各为多少? 1.单链表。 当我们知道指针p指向某结点时,能够根据该指针找到其直接后继,但是由于不知道其头指针,所以无法访问到p指针指向的结点的直接前趋。 因此无法删去该结点。 2.双链表。 由于这样的链表提供双向链接,因此根据已知结点可以查找到其直接前趋和直接后继,从而可以删除该结点。 其时间复杂度为O (1)。 3.单循环链表。 根据已知结点位置,我们可以直接得到其后相邻的结点位置(直接后继),又因为是循环链表,所以我们可以通过查找,得到p结点的直接前趋。 因此可以删去p所指结点。 其时间复杂度应为O(n)。 2.6下述算法的功能是什么? LinkListDemo(LinkListL){//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; } returnL; }//Demo 该算法的功能是: 将开始结点摘下链接到终端结点之后成为新的终端结点,而原来的第二个结点成为新的开始结点,返回新链表的头指针。 二、算法设计题 2.7设线性表的n个结点定义为(a0,a1,...an-1),重写顺序表上实现的插入和删除算法: InsertList和DeleteList. 解: #defineListSize100//假定表空间大小为100 #include #include voidError(char*message) {fprintf(stderr,"错误: %s\n",message); exit (1); }//从0开始计,表空间大小应为101了 typedefintDatatype;//假定Datatype的类型为int型 typedefstruct{ Datatypedata[ListSize];//向量data用于存放表结点 intlength;//当前的表长度 }Seqlist; //以上为定义表结构 //------------以下为要求算法---------- voidInsertList(Seqlist*L,Datatypex,inti) { //将新结点x插入L所指的顺序表的第i个结点ai的位置上 intj; if(i<0||i>L->length) Error("positionerror");//非法位置,退出 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++; } voidDeleteList(Seqlist*L,inti) {//从L所指的顺序表中删除第i个结点ai intj; if(i<0||i>L->length-1) Error("positionerror"); for(j=i+1;j L->data[j-1]=L->data[j];//结点前移 L->length--;//表长减小 } 2.8试分别用顺序表和单链表作为存储结构,实现将线性表(a0,a1,...an-1)就地逆置的操作,所谓"就地"指辅助空间应为O (1)。 2.8解: 按题意,为将线性表逆置,但辅助空间不能随表的规模增大。 我们分别讨论顺序表和单链表的情况: 1.顺序表: 要将该表逆置,可以将表中的开始结点与终端结点互换,第二个结点与倒数第二个结点互换,如此反复,就可将整个表逆置了。 算法如下: //表结构定义同上 voidReverseList(Seqlist*L) { Datatypet;//设置临时空间用于存放data inti; for(i=0;i {t=L->data[i];//交换数据 L->data[i]=L->data[L->length-1-i]; L->data[L->length-1-i]=t; } } 2.链表: 也是可以用交换数据的方式来达到逆置的目的,但是由于是单链表,数据的存取不是随机的,因此算法效率太低,我们可以利用指针的指向转换来达到表逆置的目的。 算法是这样的: //结构定义略 LinkListReverseList(LinkListhead) { //将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->next=p; } returnhead; } returnhead;//如是空表或单结点表,直接返回head } 2.9设顺序表L是一个递增有序表,试写一算法,将x插入L中,并使L仍是一个有序表。 2.9解: 因已知顺序表L是递增有序表,所以只要从头找起找到第一个比它大(或相等)的结点数据,把x插入到这个数所在的位置就是了。 算法如下: voidInsertIncreaseList(Seqlist*L,Datatypex) { inti; for(i=0;i InsertList(L,x,i);//调用顺序表插入函数 } 2.10设单链表L是一个递减有序表,试写一算法,将x插入其后仍保持L的有序性。 与上题相类似,只要从头找到第一个比x小(或相等)的结点数据,在这个位置插入就可以了。 算法如下: LinkListInsertDecreaseList(LinklistL,Datatypex) { inti; ListNode*p,*s; p=L; //查找 while(p->next&&p->next->data>x) p=p->next; s=(ListNode*)malloc(sizeof(ListNode)); s->data=x; s->next=p->next; p->next=s; returnL; } 2.11写一算法在单链表上实现线性表的ListLength(L)运算。 求单链表长只能用遍历的方法了,从头数到尾,总能数出来吧。 算法如下: intListLength(LinkListL) { intlen=0; ListNode*p; p=L;//设该表有头结点 while(p->next) { p=p->next; len++; } returnlen; } 2.12已知L1和L2分别指向两个单链表的头结点,且已知其长度分别为m和n。 试写一算法将这两个链表连接在一起,请分析你的算法的时间复杂度。 LinkListLink(LinkListL1,LinkListL2) { //将两个单链表连接在一起 ListNode*p,*q; p=L1; q=L2; while(p->next)p=p->next;//查找终端结点 p->next=q->next;//将L2的开始结点链接在L1之后 returnL1; } 本算法的主要操作时间花费在查找L1的终端结点上,与L2的长度无关,所以本算的法时间复杂度为: m+1=O(m) 2.13设A和B是两个单链表,其表中元素递增有序。 试写一算法将A和B归并成一个按元素值递减有序的单链表C,并要求辅助空间为O (1),请分析算法的时间复杂度。 根据已知条件,A和B是两个递增有序表,所以我们可以以A表为基础,按照插入单个元素的办法把B表的元素插入A表中,完成后,将表逆置就得到了一个按元素值递减有序的单链表C了。 算法如下: LinkListMergeSort(LinkListA,LinkListB) { //归并两个递增有序表为一个递减有序表 ListNode*pa,*pb,*qa,*qb; pa=A; pb=B; qa=A->next; qb=B->next; while(qa&&qb) { if(qb->data { //当B中的元素小于A中当前元素时,插入到它的前面 pb=qb; qb=qb->next;//指向B中下一元素 pa->next=pb; pb->next=qa; pa=pb; } elseif(qb->data>=pa->data&&qb->data<=qa->data) { //当B中元素大于等于A中当前元素且小于等于A中后一元素时,将此元素插入到A的当前元素之后 pa=qa; qa=qa->next;//保存A的后一元素位置 pb=qb; qb=qb->next;//保存B的后一元素位置 pa->next=pb;//插入元素 pb->next=qa; } else { //如果B中元素总是更大,指针移向下一个A元素 pa=qa; qa=qa->next; } } if(qb)//如果A表已到终端而B表还有结点未插入 { //将B表接到A表后面 pa->next=qb; } LinkListC=ReverseList(A);//调用前面2.8题所设计的逆置函数 returnC;//返回新的单链表C,已是递减排列 } 该算法的时间复杂度分析如下: 算法中只有一个while循环,在这个循环中,按照最坏的情况是B元素既有插到A的最前的,也有插到最后的,也就是说需要把A中元素和B中元素全部检查比较过,这时的所费时间就是m+n.而新链表的长度也是m+n+1(有头结点),这样逆置函数的执行所费时间为m+n+1.所以可得整个算法的时间复杂度为: m+n+m+n+1=2(m+n)+1=O(m+n) --------------------------------------------------- 以下是一位学友鲁周航提供的算法: 我的算法思路为: 当A、B都不空时,各取A、B表中的第一个结点比较,哪个值小,就把它用头插法插入C表。 (利用原来的头结点,始终是取第一个结点比较)继续取值比较。 当A表为空,或B表为空,则把剩余结点用头插法插入C表。 voidLinkList(Nodetype*A,Nodetype*B,Nodetype**C) {//假设A和B已建立,C表的头结点已初始化 Nodetype*p,*q; p=A->Next; q=B->Next; while(p&&q)//两表中都有数 { if(p->Data { A->Next=p->Next;//A指向A表的下一个结点 p->Next=C->Next;//把P的结点用头插法,插入C表。 C->Next=p; p=A->Next;//P指向A表的第一个结点,继续比较 } else { B->Next=q->Next;//算理同上面 q->Next=C->Next; C->Next=q; q=B->Next; } } if(p)//如A表还有结点,则把剩余结点,用头插法插入C表 { while(p) { A->Next=p->Next; p->Next=C->Next; C->Next=p; p=A->Next; } } else if(q)//如B表还有结点,则把剩余结点,用头插法插入B表 { while(q) { B->Next=q->Next; q->Next=C->Next; C->Next=q; q=B->Next; } } free(A);//释放原头结点空间 free(B); } 你的算法要排表两次,第一次为插入,第二次为逆序。 2.14已知单链表L是一个递增有序表,试写一高效算法,删除表中值大于min且小于max的结点(若表中有这样的结点),同时释放被删结点的空间,这里min和max是两个给定的参数。 请分析你的算法的时间复杂度。 要解这样的问题,我们首先想到的是拿链表中的元素一个个地与max和min比较,然后删除这个结点,其实因为已知其是有序链表,所以我们只要找到大于min的结点的直接前趋结点,再找到小于max的结点,然后一并把中间的全部摘掉就可以了。 算法如下: voidDeleteList(LinkListL,DataTypemin,DataType
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 数据结构 复习资料 答案