数据结构课后习题答案修订版.docx
- 文档编号:11325425
- 上传时间:2023-02-26
- 格式:DOCX
- 页数:70
- 大小:69.33KB
数据结构课后习题答案修订版.docx
《数据结构课后习题答案修订版.docx》由会员分享,可在线阅读,更多相关《数据结构课后习题答案修订版.docx(70页珍藏版)》请在冰豆网上搜索。
数据结构课后习题答案修订版
习题一答案
1.填空题
(1) 数据元素的有限集合,k上关系的有限集合
(2) 顺序存储(连续),链式存储(不连续)
(3) 有穷性,确定性,可行性,输入,输出
(4) 时间复杂度,空间复杂度
2.简述下列术语
(1) 数据——是信息的载体,它是描述客观事物的数、字符以及所有能输入到计算机中被计算机程序识别、加工处理的信息的集合。
(2) 数据元素——是数据的基本单位,是对一个客观实体的数据描述。
一个数据元素可以由一个或若干个数据项组成。
数据元素也被称为结点或记录。
(3) 数据对象——具有相同性质的数据元素的集合就是一个数据对象,它是数据的一个子集。
(4) 数据结构——数据结构就是数据之间的相互关系(即数据的组织形式)及在这些数据上定义的数据运算方法的集合。
(5) 存储结构——数据的存储结构是数据的逻辑结构在计算机内部的表示或实现,又称为数据的物理结构,它包括数据元素的表示和关系的表示。
(6) 数据类型——是具有相同性质的计算机数据的集合及定义在这个数据集合上的一组操作的总称。
3.举例说明一下数据结构和算法的关系。
通过公式:
程序=数据结构+算法我们可以比较直观地看出二者的关系,即数据结构(包个完整的程序括逻辑结构和存储结构)的设计和算法的编写是程序设计的两个关键步骤,一就是由一套合理的数据结构和建立在该结构上的算法构成的。
具体的说:
在进行程序设计之前我们首先要为待处理的数据设计一个合理的逻辑结构,进而为之设计一种适合的存储结构,因为光有逻辑结构是不够的,只有存储结构才是与计算机语言直接相关的!
有了这一套前期准备,我们才能在这个基础上设计算法,用一种计算机语言去处理这些数据,最终达到程序设计的目的。
当然,随着逻辑结构和存储结构的不同,我们设计的算法也会有所差别,这在以后的学习中会体会到。
下面通过一个简单的例子说明这种关系。
假设我们要设计一个两个n阶方阵相乘的程序:
已知两个n阶方阵A和B,我们要计算它们的乘积,得到一个新的n阶方阵C。
那么在设计这个程序之前首先想到得就是设计一种逻辑结构表示方阵,这里我们用二维数组表示它们,因为二维数组最能直观地表示这种结构;有了逻辑结构了自然还要为之设计一种存储结构,这里我们选择顺序存储方法,因为C语言对这种存储结构给予了很好的支持,例如定义一个n阶实型的二维数组A只要用floatA[n][n];这条语句就可以了,C语言在内存种为之分配了一个n*n长度的顺序存储空间(注意:
C语言默认二维数组是按行优先存储的),是不是很方便?
有了这些准备,我们就可以设计算法进行计算了,其算法如下:
voidmatrixmult(floatA[n][n],floatB[n][n],floatC[n][n])
{
inti,j,k;
floatx;
for(i=0;i { for(j=0;j { x=0; for(k=0;k { x+=A[i][k]*B[k][j]; } C[i][j]=x; } } } 通过上面这个例子我们简单的阐述了数据结构与算法的关系,更深入的内涵还要读者在以后的学习中自己慢慢地体会。 4.设有数据逻辑结构为B=(K,R),K={k1,k2,……,k9}r={ 通过以后的学习我们会知道这是一个有向图,图中共有9个结点,其中开始结点有2个,分别为K1和K2;终端结点有2个,分别为K6和K7。 逻辑结构图表示如下: k9 7.何谓算法? 试详述算法设计的目的和算法必须满足的条件。 算法是对特定问题求解步骤的一种描述,实际上是指令的有限序列,其中每一条指令表示一个或多个操作。 我们知道,程序设计的第一步是为待处理的数据设计一种合理的数据结构(包括逻辑结构和物理结构),这一步固然重要,但这不是我们的目的,设计数据结构的最终目的是为了在这个基础上编写算法进而对其进行相关的操作(例如: 遍历,查找,排序,插入,删除等),如果不去编写相应的算法那么设计数据结构也就失去了它的意义,你所输入到计算机的数据永远都是“死数据”,程序设计也就成了空谈! 一句话: 设计数据结构的目的是为了对它们进行处理,而数据处理正是通过相应的算法实现的! 总的来说,一个算法应具有以下5个重要特性: 第一,有穷性: 一个算法必须总是(对任何合法的输入值)在执行有穷步之后结束,且每一步都可在有穷时间内完成;第二,确定性: 算法中每条指令必须有确切的含义,读者理解时不会产生二意性。 并且,在任何条件下,算法只有唯一的一条执行路径,即对相同的输入只能得到相同的输出;第三,可行性: 一个算法是能行的,即算法中描述的操作都是通过已经实现的基本运算执行有限次来实现的;第四,输入: 一个算法有零个或多个的输入,这些输入取自于某个特定的对象的集合;第五,输出: 一个算法有一个或多个的输出。 这些输出是同输入有着某些特定关系的量。 8.编写一个算法,对三个两位数按由大到小的顺序进行排序,描述构造该算法的思维程。 算法如下: voidorder(shorta,shortb,shortc) { shortt; if(a if(a if(b printf(“%d%d%d\n”,a,b,c); } 算法对主函数传递过来的三个短整形变量(由于输入三个两位整数,所以定义短整形就足够了)两两进行比较,如果前面的数小于后面的数的话则进行交换,交换的中间变量用t来存放,交换的最后结果是三个变量由大到小排列,最后将结果打印输出。 习题二答案 1.填空题 (1)顺序存储结构顺序存储结构顺序存储结构链式存储结构链式存储结构 (2)p->next=p->next->next; (3)Head->next==Head(Head为链表的头结点),Head==NULL(Head为链表的第一个结点) (4)Head->next=Head->next->next(Head为链表的头结点);Head=Head->next(Head为链表的第一个结点),以上均假设链表存在至少两个结点。 (5)D(因为顺序表具有随即存取的优点) 2.动态与静态数据结构在计算机内存中的存储方式有何不同? 各有何优缺点? 参考答案: 静态存储方式(顺序存储)——逻辑相邻,物理相邻。 优点: 便于数据的随即存取,结点存储利用率高(不需要存储指针);缺点: 存取数据时要移动大量的元素,由于事先不知道存储结点的最大个数,所以应该分配尽可能大的存储空间,从而可能会造成空间浪费! 动态存储方式(链式存储)——逻辑相邻,物理不一定相邻。 优点: 动态分配和释放存储单元,避免了空间浪费,插入删除结点不需要移动大量的其他结点,只需要修改有限的指针变量;缺点: 不具备顺序存储结构随即存取的优点,查找结点需要从表头开始,一个结点的存储利用率较低,以为每个结点都要存储一个指向下个结点的指针变量。 3.描述以下三个概念的区别: 头指针、头结点、第一个结点。 参考答案: 头指针——指向头结点的指针变量;头结点——为了操作链表方便而设置的一个特殊的结点,该结点用于标示一个链表结构的存在,他不存储实在的数据;第一个结点——链表中的第一个实际存储数据的结点,区别于头结点! 在有头结点的链表中第一个结点应该是头结点后面的那个结点。 4.试写出一个计算线性链表P中结点个数的算法,其中指针P指向该表中第一个结点,尾结点的指针域为空。 intcount(datatype*p)/*假设结点存储datatype型的数据*/ { intnum=0; while(p! =NULL) { ++num; p=p->next; } returnnum; } 5.假设LA、LB为两个递增有序的线性链表,试写出将这两个线性链表归并成一个线性链表LC的操作算法。 /*注意,在主函数中已经对LC数组进行了初始化,长度等于LA和LB长度之和*/ /*假设数组LA和LB中存储着datatype类型的数据元素*/ VoidMergeList(datatypeLA[],datatypeLB[],datatypeLC[]) { inti=0,j=0,k=0; intlenthOfLA=sizeof(LA)/sizeof(datatype); intlenthOfLB=sizeof(LB)/sizeof(datatype); while(i<=lenthOfLA-1&&j<=lenthOfLB-1) { if(LA[i] { LC[k++]=LA[i++]; } else{LC[k++]=LB[j++];} } if(i<=lenthOfLA-1) { while(i<=lenthOfLA-1) { LC[k++]=LA[i++]; } } else { while(j<=lenthOfLB-1) { LC[k++]=LB[j++]; } } } 6.将学生成绩按成绩高低排列建立了一个有序单链表,每个结点包括: 学号、姓名和课程成绩。 (1)输入一个学号,如果与链表中的结点的学号相同,则将此结点删除; (2)在链表中插入一个学生的记录,使得插入后链表仍然按成绩有序排列。 算法提示: /*声明一个学生的结构体变量*/ typedefstructstudent { unsignedid; unsignedscore; charname[20]; structstudent*next; }stuNode; /*假设已经存在一个按成绩由低到高排列的有序单链表*/ (1)/*删除学号为idd的学生结点的算法,head为链表的头接点*/ voidDelete(stuNode*head,intidd) { stuNode*p=head,*q,*s; q=p;p=p->next;/*从链表的第一个结点开始查找*/ while(p! =NULL&&p->id! =idd) { q=p;p=p->next; } if(p! =NULL)/*找到要找的结点*/ { s=p;q->next=p->next;free(s);/*删除结点*/ } else{printf(“没有找到您要找的结点\n”);} } (2)/*插入学生结点node算法,假设node在主函数中已经创建*/ voidInsertNode(stuNode*head,stuNode*node) { stuNode*p=head,*q; q=p;p=p->next;/*从链表的第一个结点开始查找*/ if(node->score { node->next=p; q->next=node;/*将结点插入表头*/ return;/*算法结束*/ } while(p! =NULL&&p->score { q=p;p=p->next; } if(p==NULL) { q->next=node;/*将结点插入链表的表尾*/ } else/*插入结点*/ { node->next=p; q->next=node; } } 7.某仓库中有一批零件,按其价格从低到高的顺序构成一个单链表存于计算机内,链表的每一个结点说明同样价格的若干个零件。 现在又新有m个价格为s的零件需要进入仓库,试写出仓库零件链表增加零件的算法。 链表结点结构如下: 算法提示: /*定义一个结点,每个结点表示一种零件的相关信息*/ typedefstructNODE { floatprice;/*该零件的价格*/ unsignedlongnumber;/*该零件的数量*/ structNODE*next; }Node; /*在头结点为head的单链表中插入一个零件信息结点node的算法如下*/ /*假设该结点node已经在主函数中创建完毕*/ voidInsertNode(Node*head,Node*node) { Node*p=head,*q; q=p;p=p->next;/*从链表的第一个结点开始查找*/ if(node->price { node->next=p; q->next=node;/*将结点插入表头*/ ruturn;/*算法结束*/ } while(p! =NULL&&p->price { q=p;p=p->next; } if(p==NULL) { q->next=node;/*将结点插入链表的表尾*/ } else/*插入结点*/ { node->next=p; q->next=node; } } 8.设指针P指向单链表的首结点,指针X指向单链表中的任意一个结点,写出在X前插入一个结点i的算法。 算法提示: /*假设该链表的头结点为head,且链表中的结点用Node定义*/ /*在结点X之前插入结点i的算法如下*/ voidInsertNode(Node*head,Node*i,Node*X) { Node*p=head,*q; q=p;p=p->next;/*依照题意,p指向链表的第一个结点*/ if(p==X)/*将结点插入头结点和第一个结点之间*/ { i->next=p; q->next=i; return;/*算法结束*/ } while(p! =NULL&&p! =X)/*查找插入位置*/ { q=p;p=p->next; } if(p==NULL){returnNULL;}/*查找失败*/ else { i->next=p; q->next=i; } } 9.设多项式A和B采用线性链表的存储方式存放,试写出两个多项式相加的算法,要求把相加结果存放在A中。 算法提示: 请参考本章算法3.23和算法3.24。 10.设指针a和b分别为两个带头结点的单链表的头指针,编写实现从单连表La中删除自第i个数据元素起,共length个数据元素、并把它们插入到单链表Lb中第j个元素之前的算法。 算法提示: voidDInsert(Node*a,Node*b,inti,intj,intlength) { Node*ap=a->next,*bp=b->next;/*定义了两个分别处理La和Lb的两个指针变量*/ Node*ap1,*ap2,*ap3,*ap4;/*定义了用于处理La的4个指针变量*/ Node*bp1,*bp2;/*定义了用于处理Lb的2个指针变量*/ for(intk=1;k ap1=ap;ap2=ap1->next; ap=ap2; for(intk=1;k ap3=ap;ap4=ap3->next; ap1->next=ap4;/*将La中长度为length的子链脱链*/ for(intk=1;k bp1=bp;bp2=bp1->next; ap3->next=bp2;bp1->next=ap2;/*将长度为length的子链挂接到Lb相应的位置*/ } 11.设La和Lb是两个有序的循环链表,Pa和Pb分别指向两个表的表头结点,是写一个算法将这两个表归并为一个有序的循环链表。 算法提示: 本题可以换个角度考虑,因为对于循环链表考虑的因素较单链表多,所以我们可以将两个循环链表首先处理成两个单链表(方法: 将链表的最后一个结点的next域设置为NULL),这样问题就转化为将两个普通链表归并的问题,最后再把归并好的单链表转换成循环链表(方法: 将链表的最后一个结点的next域指向该链表的头结点)。 下面我们仅给出单链表归并的算法作为参考,具体的转换由于比较简单所以这里就不详述了: voidMergeList(Node*pa,Node*pb) { Node*pc=pa; Node*p1=pa->next,*p2=pb->next; While(p1! =NULL&&p2! =NULL) { if(p1->data { pc->next=p1;pc=pc->next;p1=p1->next; } else { pc->next=p2;pc=pc->next;p2=p2->next; } } if(p1! =NULL){pc->next=p1;} else{pc->next=p2;} } 12.已知有一个单向循环链表,其每个结点中含三个域: pre、data和next,其中data为数据域,next为指向后继结点的指针域,pre也为一个指针域,但是他的值为空(null),试编写一个算法将此单链表改为双向循环链表,即使pre成为指向前驱结点的指针域。 算法提示: /*假设链表的头指针为head,用Node来定义一个结点*/ voidConvert(Node*head) { Node*p=head,*q; q=p;p=p->next;/*从链表的第一个结点开始处理*/ while(p! =head) { p->pre=q; q=p; p=p->next; } p->pre=q;/*构成循环*/} 习题三答案 2.本题较简单,可参考本章栈操作示意图的画法,答案略。 3.参考答案: 栈和队列都是操作受限的线性表,因此二者都具备线性表的一般特性;对于栈,元素进栈出栈的原则是“后进先出”,即LIFO,对于队列,元素进出队列的原则是“先进先出”,即FIFO。 4.参考答案: 1和4。 6.算法提示: 利用栈的LIFO特性,如果是左括号则将其压入栈顶,然后读取下一个字符,如果仍然是左括号,再将其压入栈顶,依次类推,直到出现右括号为止,这时将最靠近栈顶的左括号弹出,它一定与该右括号相匹配。 下面以圆括号为例给出算法: intmatch() { seqstacks; charch; initstack(&s) while (2) { ch=getchar(); if(ch==’$’)break; if(ch==’(‘)push(&s,ch); elseif(ch==’)’) if(stackempty($s)) { printf(”theparenthessmatchfailed! \n”); return0; } elsepop(&s); } if(stackempty(&s)) { printf(”theparenthnessmatchsuccess! \n”); return1; } else { printf(”theparenthnessmatchfailed! \n”); return0; } } 8.参考答案: 这是由栈的LIFO特性决定的,可参考图4-4。 9.参考答案: 优点,不会出现线性队列中的队头指针“追上”队尾指针的情况,即“假溢出”;判空条件: 队头指针等于队尾指针(front==rear);判满条件: 队头指针指向队尾指针的下一个位置((rear+1)%MAXSIZE==front)(其中MAXSIZE为队列的长度),约定循环队列少用一个元素位。 10.参考答案: 由于队列是操作受限的线性表,所以该题可以参考第三章第五题的解答(因为线性表的一般特性同时适用于队列),除此之外,要特别注意顺序存储的队列会出现“假溢出”状态,为了解决这个问题,可以利用循环队列这种存储结构,当然对于链式存储的队列是不会出现这种问题的。 19.算法提示: typedefstruct { datatypedata[maxsize]; intfront,rear; intflag; }sequeue; (1) 初始化算法 initseq(sequeue*q) { q->data=malloc(maxsize*sizeof(datatype)); q->front=q->rear=-1; q->flag=0; } (2) 入队列算法 insertseq(sequeue*q,intx) { if(q->flag==1)returnNULL; elseif((q->rear+1)%maxsize==q->front) { q->rear=(q->rear+1)%maxsize; q->data[q->rear]=x; q->flag=1; return1; } else { q->rear=(q->rear+1)%maxsize; q->data[q->rear]=x; } } (3) 出队列算法 datatypedelqueue(sequeue*q) { if(q->flag==0)returnNULL; else { q->front=(q>front-1)%maxsize; if(q->front==q->rear){q->flag=0;} return(q->data[q->front]); } } 12.参考答案,算法提示 intG(intm,intn) { if(m==0&&n>=0)return0; elsereturnG(m-1,2n); } 栈变化示意图略。 13.算法提示: /*栈*/ datatypePop(seqstack*s) { datatypep; p=s->data[s->top-2]; s->data[s->top-2]=s->data[s->top-1]; --s->top;/*栈顶指针指向栈顶元素的下一个位置*/ returnp;/*用P返回其值*/ } /*队列*/ datatypePop(sequeue*q) { datatypep; p=q->data
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 数据结构 课后 习题 答案 修订版