- 1.08 MB
- 2022-04-22 11:32:26 发布
- 1、本文档共5页,可阅读全部内容。
- 2、本文档内容版权归属内容提供方,所产生的收益全部归内容提供方所有。如果您对本文有版权争议,可选择认领,认领后既往收益都归您。
- 3、本文档由用户上传,本站不保证质量和数量令人满意,可能有诸多瑕疵,付费之前,请仔细先通过免费阅读内容等途径辨别内容交易风险。如存在严重挂羊头卖狗肉之情形,可联系本站下载客服投诉处理。
- 文档侵权举报电话:19940600175。
'《数据结构与算法》习题答案66
目录第1章——————————————————2第2章——————————————————7第3章——————————————————13第4章——————————————————21第5章——————————————————26第6章——————————————————32第7章——————————————————42第8章——————————————————54第9章——————————————————60第10章——————————————————6466
习题11.解释下列术语:数据、数据元素、数据对象、数据结构。解:数据是用于描述客观事物的数值、字符以及一切可以输入到计算机中并由计算机程序加以处理的符号的集合,是计算机操作的对象的总称。数据元素是数据的基本单位,它是数据中的一个“个体”。有时,一个数据元素可有若干数据项组成,。数据项是数据的不可分割的最小单位。数据对象是具有相同性质的数据元素的集合,是数据的一个子集。数据结构是指相互之间存在一种或多种关系的特性相同的数据元素的集合。2.数据类型和抽象数据类型是如何定义的?两者有何异同?抽象数据类型的主要特点是什么?使用抽象数据类型的主要好处是什么?解:数据类型是一个值的集合和定义在此集合上的一组操作的总称。例如,C语言中的整型变量,其值为某个区间上的整数(依赖于机器),定义在其上的操作为加、减、乘、除和取模等算术运算。抽象数据类型(AbstractDataType,简称ADT)是指一个数学模型以及定义在此数学模型上的一组操作。例如,“整数”是一个抽象数据类型,其数学特性和具体的计算机或语言无关。“抽象”的意义在于强调数据类型的数学特性。抽象数据类型和数据类型实质上是一个概念,只是抽象数据类型的范围更广,除了已有的数据类型外,抽象数据类型还包括用户在设计软件系统时自己定义的数据类型。ADT的定义取决于它的一组逻辑特性,与其在计算机内的表示和实现无关。因此,不论ADT的内部结构如何变化,只要其数学特性不变,都不影响其外部的使用。抽象数据类型的最重要的特点是抽象和信息隐蔽。抽象的本质是抽取反映问题本质的东西,忽略非本质的细节,从而使所设计的数据结构更具有一般性,可以解决一类问题。信息隐蔽就是对用户隐蔽数据存储和操作实现的细节,使用者仅需了解抽象操作,或界面服务,通过界面中的服务来访问这些数据。一个含抽象数据类型的软件模块通常应包含定义、表示和实现三部分。3.数据元素之间的关系在计算机中有几种表示方法?各有什么特点?解:数据元素之间的关系在计算机中有四种不同的表示方法:(1)顺序存储方法。数据元素顺序存放,每个结点只含有一个元素。存储位置反映数据元素间的逻辑关系。存储密度大,但有些操作(如插入、删除)效率较差。(2)链式存储方法。每个结点除包含数据元素信息外还包含一组指针。指针反映数据元素间的逻辑关系。这种操作不要求存储空间连续,便于进行插入和删除等操作,但存储空间利用率较低。另外,由于逻辑上相邻的数据元素在存储空间上不一定相邻,所以不能对其进行随机存取。(3)索引存储方法。除数据元素存储在一地址连续的内存空间外,尚需建立一个索引表。索引表中的索引指示结点的存储位置,兼有动态和静态特性。(4)哈希(或散列)存储方法。通过哈希函数和解决冲突的方法,将关键字散列在连续的有限的地址空间内,并将哈希函数的值作为该数据元素的存储地址。其特点是存取速度快,只能按关键字随机存取,不能顺序存储,也不能折半存取。66
4.简述数据结构的三个层次、五个要素。解:数据结构的三个层次是指抽象、实现和评价三个层次,五个要素是指逻辑结构、存储结构、基本运算、算法和不同数据结构的比较与算法分析五个方面。5.举一个数据结构的例子,说明其逻辑结构、存储结构及其运算三个方面的内容。并说明数据的逻辑结构、存储结构及其运算之间的关系。解:例如复数数据结构,其逻辑结构是复数的表示,而存储结构是指复数在计算机内的表示,运算是指对复数初始化、相加等操作。数据的逻辑结构反映数据元素之间的逻辑关系。数据的存储结构是数据结构在计算机中的表示,包括数据元素的表示及其关系的表示。数据的运算是对数据定义的一组操作,运算是定义在逻辑结构上的,和存储结构无关,而运算的实现则依赖于存储结构。6.设为整数,试给出下列各程序段中标号为@的语句的频度。(1)i=1;while(ij)j++;elsei++;}(5)x=n;y=0;//n是不小于1的常数while(x>=(y+1)*(y+1)){@y++;}(6)x=91;y=100;while(y>0){66
@if(x>100){x-=10;y--;}elsex++;}解:(1)ëû;(2)n-1;(3)n-1;(4)éù;(5)ëû;(6)1007.调用下列C函数,回答下列问题:(1)试指出值的大小,并写出值的推导过程。(2)假定=5,试指出值的大小和执行时的输出结果。intf(intn){inti,j,k,sum=0;for(i=1;ii-1;j--)for(k=1;knext。(1)while(p!=NULL&&p->data!=x)p=p->next;if(p==NULL)returnNULL;//查找失败elsereturnp;//查找成功(2)while(p!=NULL&&p->datanext;if(p==NULL||p->data>x)returnNULL;//查找失败elsereturnp;//查找成功(3)while(p!=NULL&&p->data>x)p=p->next;if(p==NULL||p->datanext;//L是一带头结点的单链表,结点有数据域data和指针域nextif(pre!=NULL)while(pre->next!=NULL){p=pre->next;if(p->data>=pre->data)pre=p;66
elsereturn(FALSE);}return(TUURE);解:该算法的功能是判断链表L是否非递减有序,若是则返回TRUE,否则返回FALSE。pre指向当前结点,p指向pre的后继。7.设、分别指向两个带头结点的有序(从小到大)单链表。阅读下列程序,并回答问题:(1)程序的功能;(2)、2中的值的含义;(3)、中值的含义。voidexam(LinkListpa,LinkListpb){p1=pa->next;p2=pb->next;pa->next=NULL;s1=0;s2=0;while(p1&&p2){switch{case(p1->datadata):p=p1;p1=p1->next;s2++;deletep;case(p1->data>p2->data):p2=p2->next;case(p1->data=p2->data):p=p1;p1=p1->next;p2->next=p2->next;pa->next=p;p2=p2->next;s1++;}while(p1){p=p1;p1=p1->next;deletep;s2++;}}}解:本程序的功能是将pa和pb链表中值相同的结点保留在pa链表中(pa中与pb中不同的结点删除),pa是结果链表的头指针。链表中结点值与从前逆序。s1记结果链表中结点个数(即pa与pb中相等的元素个数),s2记原pa链表中删除的结点个数。8.假设长度大于1的循环单链表中,既无头结点也无头指针,为指向该链表中某一结点的指针,编写一算法删除该结点的前驱结点。解:intDelete_Pre(LNode*p){LNode*q,*temp;q=p;while(q->next->next!=p)q=q->next;temp=q->next;q->next=p;deletetemp;returnOK;}66
9.已知两个单链表La和Lb分别表示两个集合,其元素递增排列。编写一算法求其交集Lc,要求Lc以元素递减的单链表形式存储。解:voidInter_set(LinkListLa,LinkListLb,LinkList&Lc){LNode*pa,*pb,*pc;pa=La->next;pb=Lb->next;Lc=newLNode;Lc->next=NULL;while(pa&&pb){if(pa->data==pb->data){pc=newLNode;pc->data=pa->data;pc->next=Lc->next;Lc->next=pc;pa=pa->next;pb=pb->next;}elseif(pa->datadata)pa=pa->next;elsepb=pb->next;}}10.已知单链表是一个递增有序表,试写一高效算法,删除表中值大于min且小于max的结点(若表中有这样的结点),同时释放被删除结点的空间,这里min和max是两个给定的参数。请分析你的算法的时间复杂度。解:voidDelete_Between(LinkList&L,intmin,intmax){LNode*p,*q,*s;p=L;while(p->next->data<=min)p=p->next;if(p){q=p->next;while(q&&q->datanext;deletes;}p->next=q;}}11.已知非空单链表的头指针为,试写一算法,交换所指结点与其下一个结点在链表中的位置(设66
指向的不是链表最后那个结点)。解:voidExchange(LinkList&L,LNode*p){LNode*q,*pre;q=L->next;pre=L;while(q!=NULL&&q!=p){pre=q;q=q->next;}if(p->next==NULL)printf("p无后继结点n");else{q=p->next;pre->next=q;p->next=q->next;q->next=p;}}12.线性表中有n个元素,每个元素是一个字符,现存于数组R[n]中,试写一算法,使R中元素的字符按字母字符、数字字符和其它字符的顺序排列。要求利用原来的空间,元素移动次数最小。解:voidprocess(charR[],intn){intlow,high;chark;low=0;high=n-1;while(lownext;pre=L;q=p;while(p->next!=NULL){if(p->next->datadata){pre=p;q=p->next;}p=p->next;}pre->next=q->next;deleteq;}14.已知两个单链表La和Lb,其元素递增排列。编写一算法,将La和Lb归并成一个单链表Lc,其元素递减排列(允许表中有相同的元素),要求不另辟新的空间。解:voidinteraction(LinkList&La,LinkListLb){LNode*pa,*pb,*s;pa=La->next;pb=Lb->next;La->next=NULL;deleteLb;while(pa&&pb)if(pa->datadata){s=pa;pa=pa->next;s->next=La->next;La->next=s;}else{s=pb;pb=pb->next;s->next=La->next;La->next=s;}while(pa){s=pa;pa=pa->next;s->next=La->next;La->next=s;}while(pb){s=pb;pb=pb->next;s->next=La->next;La->next=s;}}15.设以带头结点的双向循环链表表示的线性表。试写一时间复杂度为的算法,将改造为。解:voidOEReform(CirDuLinkList&L){DuLNode*p;p=L->next;while(p->next!=L&&p->next->next!=L){p->next=p->next->next;p=p->next;}if(p->next==L)p->next=L->prior->prior;66
elsep->next=L->prior;p=p->next;while(p->prior->prior!=L){p->next=p->prior->prior;p=p->next;}p->next=L;for(p=L;p->next!=L;p=p->next)p->next->prior=p;L->prior=p;}16.设有一双向循环链表,每个结点除有prior、data和next三个域外,还有一个访问频度域frep。在链表被起用之前,频度域frep的值为0,而每当对链表进行一次LocateElem(L,e)的操作后,被访问的结点的频度域frep的值增1,同时调整链表中结点之间的顺序,使其按访问频度非递增的次序顺序排列,以便始终保持被频繁访问的结点总是靠近表头结点。试写出符合上述要求的LocateElem操作的算法。解:voidLocateElem(CirDuLinkList&L,intx){DuLNode*p=L->next,*q;while(p->data!=x&&p)p=p->next;if(p){p->frep++;q=p->prior;while(q->frepfrep&&q!=L)q=q->prior;if(q!=p->prior){p->prior->next=p->next;p->next->prior=p->prior;q->next->prior=p;p->next=q->next;q->next=p;p->prior=q;}}}习题31.名词解释:栈、队列、循环队列。解:栈是只能在一端进行插入和删除操作的线性表,允许插入和删除的一端叫栈顶,另一端叫栈底。最后插入的元素最先删除,故栈也称后进先出表。66
队列是允许在一端插入而在另一端删除的线性表,允许插入的一端叫队尾,允许删除的一端叫队头。最后插入的元素最先删除,故栈也称先进先出表。最先入队的元素最先删除,故队列也称先进先出表。用常规意义下顺序存储结构的一维数组表示队列,由于队列的性质(队尾插入,队头删除),容易造成“假溢出”现象,即队尾已达到一维数组的高下标,不能再插入,然而队中元素个数小于队列的长度。循环队列是解决“假溢出”的一种方法。通常把一维数组看成首尾相接。在循环队列下,通常采用“牺牲一个存储空间”的方法解决“队满”和“队空”的判定问题。2.如果输入序列为1,2,3,4,5,6,试问能否通过栈结构得到以下两个序列:4,3,5,6,1,2和1,3,5,4,2,6;请说明为什么不能或如何才能得到。解:输入序列为1,2,3,4,5,6,不能得到4,3,5,6,1,2,其理由是:输出序列最后两个元素是1,2,前面四个元素(4,3,5,6)得到后,栈中元素剩下1,2,且2在栈顶,栈底元素1不可能在栈顶元素2出栈之前出栈。得到序列1,3,5,4,2,6的过程是:1入栈并出栈;然后2和3依次入栈,3出栈,部分输出序列是1,3;紧接着4和5入栈,5,4和2依次出栈,此时输出序列为1,3,5,4,2;最后6入栈并出栈,得到最终结果序列是1,3,5,4,2,6。3.试证明:若借助栈由输入序列1,2,…,得到序列,,…,(它是输入序列的一个全排列),则在输出序列中不可能出现下列情形:存在着<<,使得<<。解:如果<,说明在入栈前先出栈。而对于>的情况,则说明要将压到之上,也就是在出栈之后才能出栈。这说明,对于<<,不可能出现<<的输出序列。4.当函数递归调用自身时,函数内定义的局部变量在函数的2次调用期间是否占用同一数据区?为什么?解:函数递归调用自身时,函数内定义的局部变量在函数的2次调用期间不占用同一数据区。每次调用都保留其数据区,这是由递归定义所决定的,用“递归工作栈”来实现。5.简要叙述循环队列的数据结构,并写出其初始状态、队列空、队列满时的队头指针和队尾指针的值。解:循环队列的数据结构略。typedefstruct{ElemType*elem;intfront;intrear;}SqQueue,Q;(1)初始状态:Q.front=Q.rear=0;(2)队列空:Q.front=Q.rear=0;(3)队列满:Q.front=(Q.rear+1)%MAXSIZE;6.设一个双端队列,元素进入该队列的次序为1,2,3,4.求既不能由输入受限的双端队列得到,又不能由输出受限的双端队列得到的输出序列。66
解:既不能由输入受限的双端队列得到,又不能由输出受限的双端队列得到的输出序列是:4,2,3,1。7.简述以下算法的功能。(1)voidalgo1(StackS){inti,n,A[255];n=0;while(!StackEmpty(S)){n++;Pop(S,A[n]);};for(i=1;i<=n;i++)Push(S,A[i]);}(2)voidalgo2(StackS,inte){StackT;intd;InitStack(T);while(!StackEmpty(S)){Pop(S,d);if(d!=e)Push(T,d);}while(!StackEmpty(T)){Pop(T,d);Push(S,d);}}(3)voidalgo3(Queue&Q){//栈和队列的元素类型均为intStackS;intd;InitStack(T);while(!QueueEmpty(Q)){DeQueue(Q,d);Push(S,d);}while(!StackEmpty(S)){Pop(S,d);EnQueue(Q,d);}}解:(1)将栈中元素逆置。(2)将栈中的0元素删除。(3)将队列中元素逆置。8.试写一个算法,识别依次读入的一个以@为结束符的字符序列是否为形如“序列1&序列2”模式的字符序列。其中,序列1和序列2中不含字符@,且序列2是序列1的逆序列。解:intIsReverse(){//判断输入的字符串中"&"前和"&"后部分是否为逆串,是则返回1,否则返回0SqStacks;charc,x;InitStack(s);while((c=getchar())!="&")Push(s,c);while((c=getchar())!="@"){if(StackEmpty(s))return0;Pop(s,x);if(x!=c)return0;66
}if(!StackEmpty(s))return0;return1;}9.在火车调度站的入口处有节硬席或软席车厢(分别以H和S表示)等待调度,试写一算法,输出对这节车厢进行调度的操作(即入栈或出栈操作)序列,以使所有的软席车厢都被调整到硬席车厢之前。解:typedefcharSS[MAX];voidTrain_arrange(SS&train){//这里用字符串train表示火车,"H"表示硬席,"S"表示软席SqStacks;char*p,*q,c;p=train;q=train;InitStack(s);while(*p){if(*p=="H")Push(s,*p);//把"H"存入栈中else*(q++)=*p;//把"S"调到前部p++;}while(!StackEmpty(s)){Pop(s,c);*(q++)=c;//把"H"接在后部}}10.试写出求递归函数的递归算法,并消除递归:解:intF_recursive(intn,int&s){//递归算法if(n<0)returnERROR;if(n==0)s=n+1;else{F_recursive(n/2,s);s=n*s;}returnOK;66
}//F_recursivetypedefstruct{ElemTypea;ElemTypeb;}node;typedefstruct{node*data;inttop;//栈顶指针intstacksize;}SqStack;voidF_nonrecursive(intn,int&s){//非递归算法SqStackT;nodex,t;if(n<0)exit(0);if(n==0)s=n+1;else{InitStack(T);while(n!=0){x.a=n;x.b=n/2;Push(T,x);n=x.b;}//whiles=1;while(!StackEmpty(T)){Pop(T,t);s*=t.a;}//while}}//F_nonrecursive11.试将下列递归函数改为非递归函数。voidtest(int&sum){intx;scanf("%d",&x);if(x==0)sum=0;else{test(sum);sum+=x;}66
printf("sum=%dn",sum);}解:voidtest(){intx,sum=0,top=-1,s[10];scanf("%d",&x);while(x!=0){s[++top]=x;scanf("%d",&x);}printf("sum=%d",sum);while(top>-1){sum+=s[top--];printf("sum=%dn",sum);}};12.设整数列,,…,,给出求解最大值的递归程序。解:intMaxValue(inta[],intn){intmax;if(n==1)max=a[0];elseif(a[n-1]>MaxValue(a,n-1))max=a[n-1];elsemax=MaxValue(a,n-1);returnmax;}13.假设以带头结点的循环链表表示队列,并且只设一个指针指向队尾元素结点(注意不设头指针),试编写相应的队列初始化、入队和出队的算法。解:(1)voidInitQueue(CirLinkList&rear){rear=newLNode;rear->next=rear;}(2)voidEnQueue(CirLinkList&rear,ElemTypex){LNode*s;s=newLNode;s->data=x;s->next=rear->next;rear->next=s;rear=s;66
}(3)voidDnQueue(CirLinkList&rear,ElemType&x){LNode*s;if(rear->next==rear){printf("队空n");exit(0);}s=rear->next->next;//s指向队头元素rear->next->next=s->next;//队头元素出队x=s->data;if(s==rear)rear=rear->next;//空队deletes;}14.假设称正读和反读都相同的字符序列为“回文”,试写一个算法判别读入的一个亿@为结束符的字符序列是否是“回文”。解:intTest(){//判别输入的字符串是否回文序列,是则返回1,否则返回0SqStackS;SqQueueQ;charc;ElemTypea,b;InitStack(S);InitQueue(Q);while((c=getchar())!="@"){Push(S,c);EnQueue(Q,c);//同时使用栈和队列两种结构}while(!StackEmpty(S)){Pop(S,a);DeQueue(Q,b);if(a!=b)returnERROR;}returnOK;}15.写一算法,借助于栈将一个单链表逆序输出。解:voidprocess(LinkList&L){LNode*p;SqStacks;66
ElemTypex;p=L->next;InitStack(s);while(p){Push(s,p->data);p=p->next;}while(!StackEmpty(s)){Pop(s,x);printf("%d",x);}}16.假设循环队列中只设rear和length来分别指示队尾元素的位置和队中元素的个数,试给出此循环队列的队满条件,并写出相应的入队和出队算法,要求出队时需返回队头元素。解:typedefstruct{ElemType*base;//动态分配存储空间intlength;//队列长度intrear;//尾指针,指向队列尾元素}SqQueue;intEnQueue(SqQueue&Q,ElemTypex){//带length域的循环队列入队算法if(Q.length==MAX)returnERROR;//队列满Q.rear=(Q.rear+1)%MAX;Q.base[Q.rear]=x;Q.length++;returnOK;}intDeQueue(SqQueue&Q,ElemType&x){//带length域的循环队列出队算法inthead;if(Q.length==0)returnERROR;//队列空head=(Q.rear-Q.length+1)%MAX;x=Q.base[head];Q.length--;returnOK;}intInitQueue(SqQueue&Q){//构造一个空循环队列QQ.base=newElemType[MAX];if(!Q.base)exit(OVERFLOW);Q.rear=0;Q.length=0;returnOK;66
}习题41.名词解释:串、空串、空格串、子串。解:串是有限的字符序列,从数据结构角度讲,串属于线性结构。与线性表的不同之处在于串的元素是字符。空串是不含任何字符的串,其长度为0。66
空格是一个字符,其ASCII码值是32。空格串是由空格组成的串,其长度等于空格的个数。串中任意连续的若干字符组成的子序列称为该串的子串。2.已知三个字符串分别为,,。利用串的基本运算得到结果串为,要求写出得到结果串所用的函数及执行算法。解:串可看作由以下两部分组成:和,设这两部分分别叫串s1和s2,要设法从、、中得到这两部分,然后使用连接操作连接s1和s2得到。i=index();//s1=substr(,i,length()-i+1);//取出串s1j=index(,);//求串在串中的起始位置,串中后是s2=substr(,j+3,length()-j-2);//形成串s2=concat(s1,s2);3.已知字符串中存放一段英文,写出算法,将其按给定的长度格式化成两端对齐的字符串,其多余的字符存入。解:题目要求将字符串S1拆分成字符串S2和S3,要求字符串S2“按给定长度n格式化为两端对齐的字符串”,即长度为n且首尾字符不能为空格字符。算法从左到右扫描字符串S1,找到第一个非空格字符,计数到n,第n个拷入字符串S2的字符不能为空格,然后将余下字符复制到字符串S3中。voidformat(char*S1,char*S2,char*S3,intn){char*p=S1,*q=S2;inti=0;while(*p!=" "&&*p=="")p++;//滤掉S1左端空格if(*p==" "){printf("字符串S1为空串或空格串n");exit(0);}while(*p!=" "&&i