数据结构课程课后习题答案Word文档格式.docx
《数据结构课程课后习题答案Word文档格式.docx》由会员分享,可在线阅读,更多相关《数据结构课程课后习题答案Word文档格式.docx(89页珍藏版)》请在冰豆网上搜索。
![数据结构课程课后习题答案Word文档格式.docx](https://file1.bdocx.com/fileroot1/2022-11/24/17bdb260-a081-430f-b2f2-249484039694/17bdb260-a081-430f-b2f2-2494840396941.gif)
(1)数据结构和数据类型两个概念之间有区别吗?
简单地说,数据结构定义了一组按某些关系结合在一起的数组元素的集合。
数据类型不仅定义了一组数据元素,而且还在其上定义了一组操作。
(2)简述线性结构、树形结构和图形结构的不同点。
线性结构反映结点间的逻辑关系是一对一的,树形线性结构反映结点间的逻辑关系是一对多的,图在结构反映结点间的逻辑关系是多对多的。
(3)设有采用二元组表示的数据逻辑结构S=(D,R),其中D={a,b,…,i},R={(a,b),(a,c),(c,d),(c,f),(f,h),(d,e),(f,g),(h,i)},问相对于关系R,哪些结点是开始结点,哪些结点是终端结点?
该逻辑结构为树形结构,其中a结点没有前驱结点,称为根结点,b、e、g、i结点没有后继结点,是终端结点,也称为叶子结点。
(4)以下各函数是算法中语句的执行频度,n为问题规模,给出对应的时间复杂度:
T1(n)=nlog2n-1000log2n
T2(n)=
-1000log2n
T3(n)=n2-1000log2n
T4(n)=2nlog2n-1000log2n
T1(n)=O(nlog2n),T2(n)=O(),T3(n)=O(n2),T4(n)=O(nlog2n)。
(5)分析下面程序段中循环语句的执行次数。
intj=0,s=0,n=100;
do
{j=j+1;
s=s+10*j;
}while(j<
n&
&
s<
n);
j=0,第1次循环:
j=1,s=10。
第2次循环:
j=2,s=30。
第3次循环:
j=3,s=60。
第4次循环:
j=4,s=100。
while条件不再满足。
所以,其中循环语句的执行次数为4。
(6)执行下面的语句时,语句s++的执行次数为多少?
ints=0;
for(i=1;
i<
n-1;
i++)
for(j=n;
j>
=i;
j--)
s++;
语句s的执行次数
。
(7)设n为问题规模,求以下算法的时间复杂度。
voidfun1(intn)
{intx=0,i;
for(i=1;
=n;
for(j=i+1;
j<
j++)
x++;
}
其中x++语句属基本运算语句,
=O(n2)。
(8)设n为问题规模,是一个正偶数,试计算以下算法结束时m的值,并给出该算法的时间复杂度。
voidfun2(intn)
{intm=0;
for(j=2*i;
m++;
由于内循环j的取值范围,所以i≤n/2,则
,该程序段的时间复杂度为O(n2)。
上机实验题1
有一个整型数组a,其中含有n个元素,设计尽可能好的算法求其中的最大元素和次大元素,并采用相关数据测试。
解:
maxs算法用于返回数组a[0..n-1]中的最大元素值max1和次大元素值max2,max1和max2设计为引用类型。
对应的程序如下:
#include<
stdio.h>
voidmaxs(inta[],intn,int&
max1,int&
max2)
{inti;
max1=max2=a[0];
n;
if(a[i]>
max1)
{max2=max1;
max1=a[i];
}
elseif(a[i]>
max2=a[i];
voidmain()
{inta[]={1,4,10,6,8,3,5,7,9,2};
intn=10;
intmax1,max2;
maxs(a,n,max1,max2);
printf("
最大元素值=%d,次大元素值=%d\n"
max1,max2);
练习题2
(1)数据在计算机存储器内表示时,物理地址与逻辑地址相对顺序相同并且是连续的,称之为()。
A.存储结构B.逻辑结构C.顺序存储结构D.链式存储结构
(2)在n个结点的顺序表中,算法的时间复杂度是O
(1)的操作是()。
A.访问第i个结点(1≤i≤n)和求第i(2≤i≤n)个结点的前驱结点
B.在第i(1≤i≤n)个结点后插入一个新结点
C.删除第i个结点(1≤i≤n)
D.将n个结点从小到大排序
(3)向一个有127个元素的顺序表中插入一个新元素并保持原来顺序不变,平均要移动()个元素。
A.8B.63.5C.63D.7
(4)链式存储结构所占存储空间()。
A.分两部分,一部分存放结点值,另一部分存放表示结点间关系的指针
B.只有一部分,存放结点值
C.只有一部分,存储表示结点间关系的指针
D.分两部分,一部分存放结点值,另一部分存放结点所占单元数
(5)线性表若采用链式存储结构时,要求内存中可用存储单元的地址()。
A.必须是连续的B.部分地址必须是连续的
C.一定是不连续的D.连续或不连续都可以
(6)一个线性表在()情况下适用于采用链式存储结构。
A.需经常修改其中的结点值B.需不断对其进行删除插入
C.其中含有大量的结点D.其中结点结构复杂
(7)单链表的存储密度()
A.大于1B.等于1C.小于1D.不能确定
(1)在顺序表中插入或删除一个元素时,需要平均移动(①)元素,具体移动的元素个数与(②)有关。
①表中一半②表长和该元素在表中的位置
(2)向一个长度为n的顺序表的第i个元素(1≤i≤n+1)之前插入一个元素时,需向后移动()个元素。
n-i+1
(3)向一个长度为n的顺序表中删除第i个元素(1≤i≤n)时,需向前移动()个元素。
n-i
(4)在顺序表中访问任意一个元素的时间复杂度均为(①),因此顺序表也称为(②)的数据结构。
①O
(1)②随机存取
(5)顺序表中逻辑上相邻的元素的物理位置(①)相邻。
单链表中逻辑上相邻的元素的物理位置(②)相邻。
①一定②不一定
(6)在带头结点的单链表中,除头结点外,任一结点的存储位置由()指示。
其前驱结点的链域的值
(7)在含有n个数据结点的单链表中要删除已知结点*p,需找到它的(①),其时间复杂度为(②)。
①前驱结点的地址②O(n)
(8)含有n(n>
1)个结点的循环双向链表中,为空的指针域数为()。
(1)试比较顺序存储结构和链式存储结构的优缺点。
在什么情况下用顺序表比链表好?
顺序存储结构中,相邻数据元素的存放地址也相邻,并要求内存中可用存储单元的地址必须是连续的。
其优点是存储密度大,存储空间利用率高;
缺点是插入或删除元素时不方便。
链式存储结构中,相邻数据元素可随意存放,但所占存储空间分两部分,一部分存放结点值,另一部分存放表示结点间关系的指针。
其优点是插入或删除元素时很方便,使用灵活;
缺点是存储密度小,存储空间利用率低。
顺序表适宜于做查找这样的静态操作;
链表宜于做插入、删除这样的动态操作。
若线性表的长度变化不大,且其主要操作是查找,则采用顺序表;
若线性表的长度变化较大,且其主要操作是插入、删除操作,则采用链表。
(2)对于表长为n的顺序表,在任何位置上插入或删除一个元素的概率相等时,插入一个元素所需要移动的元素的平均个数为多少?
删除一个元素所需要移动的平均个数为多少?
插入一个元素所需要移动的元素的平均个数为(n-1)/2,删除一个元素所需要移动的平均个数为n/2。
(3)在链表中设置头结点的作用是什么?
在链表中设置头结点后,不管链表是否为空表,头结点指针均不空,并使得对链表的操作(如插入和删除)在各种情况下统一,从而简化了算法的实现过程。
(4)对于双链表和单链表,在两个结点之间插入一个新结点时需修改的指针各为多少个?
对于双链表,在两个结点之间插入一个新结点时,需修改前驱结点的next域、后继结点的prior域和新插入结点的next、prior域。
所以共修改4个指针。
对于单链表,在两个结点之间插入一个新结点时,需修改前一结点的next域,新插入结点的next域。
所以共修改两个指针。
(5)某含有n(n>
1)结点的线性表中,最常用的操作是在尾结点之后插入一个结点和删除第一个结点,则采用以下哪种存储方式最节省运算时间。
①单链表;
②仅有头指针不带头结点的循环单链表;
③双链表;
④仅有尾指针的循环单链表。
在单链表中,删除第一个结点的时间复杂度为O
(1)。
插入结点需找到前驱结点,所以在尾结点之后插入一个结点,需找到尾结点,对应的时间复杂度为O(n)。
在仅有头指针不带头结点的循环单链表中,删除第一个结点的时间复杂度O(n),因为删除第一个结点后还要将其改为循环单链表;
在尾结点之后插入一个结点的时间复杂度也为O(n)。
在双链表中,删除第一个结点的时间复杂度为O
(1);
在尾结点之后插入一个结点,也需找到尾结点,对应的时间复杂度为O(n)。
在仅有尾指针的循环单链表中,通过该尾指针可以直接找到第一个结点,所以删除第一个结点的时间复杂度为O
(1);
在尾结点之后插入一个结点也就是在尾指针所指结点之后插入一个结点,时间复杂度也为O
(1)。
因此④最节省运算时间。
4.算法设计题
(1)设计一个高效算法,将顺序表的所有元素逆置,要求算法空间复杂度为O
(1)。
遍历顺序表L的前半部分元素,对于元素L.data[i](0≤i<L.length/2),将其与后半部分对应元素L.data[L.length-i-1]进行交换。
对应的算法如下:
voidreverse(SqList&
L)
ElemTypex;
for(i=0;
L.length/2;
{x=L.data[i];
//L.data[i]与L.data[L.length-i-1]交换
L.data[i]=L.data[L.length-i-1];
L.data[L.length-i-1]=x;
}
本算法的时间复杂度为O(n)。
(2)设计一个算法从顺序表中删除重复的元素,并使剩余元素间的相对次序保持不变。
对于顺序表L,用i从1开始遍历其元素,设L.data[0..j](j的初值为0)中没有重复的元素。
检测L.data[i](j<
L.length),若L.data[i]和L.data[0..j]中任何一个元素都不相同,则将L.data[i]存入L.data[j+1]中。
voiddelsame(SqList&
L)//L为引用型参数
{inti,j=0,k;
L.length;
{k=0;
while(k<
=j&
L.data[k]!
=L.data[i])
k++;
if(k>
j)//表示L.data[i]和L.data[0..j]中所有元素都不相同
{j++;
L.data[j]=L.data[i];
L.length=j+1;
//顺序表长度置新值
本算法的时间复杂度为O(n2),空间复杂度为O
(1)。
(3)设计一个算法从有序顺序表中删除重复的元素,并使剩余元素间的相对次序保持不变。
在有序顺序表L中,所有重复的元素应是相邻存放的,用k保存不重复出现的元素个数,先将不重复的有序区看成是L.data[0..0],置e=L.data[0],用i从1开始遍历L的所有元素:
当L.data[i]≠e时,将它放在L.data[k]中,k增1,置e=L.data[i],最后将L的length置为k。
voiddelsame1(SqList&
{inti,k=1;
//k保存不重复的元素个数
ElemTypee;
e=L.data[0];
{if(L.data[i]!
=e)//只保存不重复的元素
{L.data[k]=L.data[i];
e=L.data[i];
L.length=k;
//顺序表长度置新值
本算法是一个高效算法,其时间复杂度为O(n),空间复杂度为O
(1)。
如果每次遇到重复的元素,都通过移动其后所有元素来删除它,这样的时间复杂度会变成O(n2)。
(4)设计一个算法删除单链表L中第一个值为x的结点。
用p、q遍历整个单链表,p指向*q的前驱结点,q用于查找第一个值为x的结点,当找到后将*q结点删除,返回1;
否则返回0。
intdelx(SLink*&
L,ElemTypex)
{SLink*p=L,*q=p->
next;
//p指向*q的前驱结点
while(q!
=NULL&
q->
data!
=x)
{p=q;
q=q->
if(q!
=NULL)//找到值为x的结点
{p->
next=q->
free(q);
return1;
elsereturn0;
//未找到值为x的结点
(5)设计一个算法判定单链表L是否是递增的。
判定链表L从第2个结点开始的每个结点的值是否比其前驱的值大。
若有一个不成立,则整个链表便不是递增的;
否则是递增的。
intincrease(SLink*L)
{SLink*pre=L->
next,*p;
//pre指向第一个数据结点
p=pre->
//p指向*pre结点的后继结点
while(p!
=NULL)
{if(p->
data>
=pre->
data)//若正序则继续判断下一个结点
{pre=p;
//pre、p同步后移
p=p->
elsereturn0;
return1;
(6)有一个整数元素建立的单链表A,设计一个算法,将其拆分成两个单链表A和B,使得A单链表中含有所有的偶数结点,B单链表中所有的奇数结点,且保持原来的相对次序。
采用重新单链表的方法,由于要保持相对次序,所以采用尾插法建立新表A、B。
用p遍历原单链表A的所有数据结点,若为偶数结点,将其链到A中,若为奇数结点,将其链到B中。
voidSplit(SLink*&
A,SLink*&
B)
{SLink*p=A->
next,*ra,*rb;
ra=A;
B=(SLink*)malloc(sizeof(SLink));
//建立头结点
rb=B;
//r总是指向B链表的尾结点
data%2==0)//偶数结点
{ra->
next=p;
//将*p结点链到A中
ra=p;
else//奇数结点
{rb->
//将*p结点链到B中
rb=p;
ra->
next=rb->
next=NULL;
本算法的时间复杂度为O(n),空间复杂度为O
(1)。
(7)有一个有序单链表(从小到大排列),表头指针为L,设计一个算法向该单链表中插入一个元素为x的结点,使插入后该链表仍然有序。
先建立一个待插入的结点,然后依次与链表中的各结点的数据域比较大小,找到插入该结点的位置,最后插入该结点。
voidinorderList(SLink*&
{SLink*s,*p,*q;
s=(SLink*)malloc(sizeof(SLink));
//建立一个待插入的结点
s->
data=x;
s->
if(L==NULL||x<
L->
data)//若单链表为空或x小于第1个结点date域
{s->
next=L;
//把*s结点插入到头结点之后
L=s;
else
{q=L;
//寻找插入位置,p指向待比较的结点,q指向p的前驱结点
p=q->
while(p!
x>
p->
data)//若x小于p所指结点的data域值
if(x>
data)
{q=p;
p=p->
}
s->
//将s结点插入到*q和*p之间
q->
next=s;
(8)有一个单链表L,其中可能出现值域重复的结点,设计一个算法删除值域重复的结点。
并分析算法的时间复杂度。
用p遍历单链表,用r遍历*p结点之后的结点,q始终指向*r结点的直接前驱结点,若r->
data==p->
data,则删除*r结点,否则q、r同步后移一个结点。
voiddels1(SLink*&
{SLink*p=L->
next,*q,*r,*t;
{q=p;
r=q->
while(r!
{if(r->
data)//r指向被删结点
{t=r->
q->
next=t;
free(r);
r=t;
else
{q=r;
r=r->
p=p->
本算法的时间复杂度为O(n2)。
(9)有一个递增有序单链表(允许出现值域重复的结点),设计一个算法删除值域重复的结点。
由于是有序表,所以相同值域的结点都是相邻的。
用p遍历递增单链表,若*p结点的值域等于其后结点的值域,则删除后者。
voiddels(SLink*&
next,*q;
while(p->
next!
=NULL)
next->
data)//找到重复值的结点
{q=p->
//q指向这个重复值的结点
p->
//删除*q结点
free(q);
elsep=p->
(10)有一个双链表L,设计一个算法查找第一个元素值为x的结点,将其与后继结点进行交换。
先找到第一个元素值为x的结点*p,q指向其后继结点,本题是将*p结点移到*q结点之后,实现过程是:
删除*p结点,再将其插入到*q结点之后。
intswap(DLink*L,ElemTypex)
{DLink*p=L->
p->
if(p==NULL)//未找到值为x的结点
return0;
else//找到值为x的结点*p
{q=p->
//q指向*p的后继结点
if(q!
=NULL)//*p结点不是尾结点
{p->
prior->
next=q;
//先删除*p结点
q->
prior=p->
prior;
//将*p结点插入到*q结点之后
if(q->
prior=p;
prior=q;
return1;
else//*p结点是尾结点
return0;
//无法与后继结点交换,返回0
(11)对于有n(n≥1)个数据结点的循环单链表L,设计一个算法将所有结点逆置。
采用头插法重建循环单链表L的思路,先建立一个空的循环单链表,用p遍历所有数据结点,每次将*p结点插入到前端。
voidReverse(SLink*&
L->
//建立一个空循环单链表
=L)
p->
next=L->
//将*p结点插入到前端
L->
p=q;
上机实验题2
有两个整数集合采用有序单