(0012)《数据结构》复习思考题答案

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

(0012)《数据结构》复习思考题答案

一、

常用的存储表示方法有哪几种?

常用的存储表示方法有四种:

顺序存储方法:它是把逻辑上相邻的结点存储在物理位置相邻的存储单元里,结点间的逻辑关系由存储单元的邻接关系来体现。由此得到的存储表示称为顺序存储结构。

链接存储方法:它不要求逻辑上相邻的结点在物理位置上亦相邻,结点间的逻辑关系是由附加的指针字段表示的。由此得到的存储表示称为链式存储结构。

索引存储方法:除建立存储结点信息外,还建立附加的索引表来标识结点的地址。

散列存储方法:就是根据结点的关键字直接计算出该结点的存储地址。

二、

下列程序段带标号语句的频度和时间复杂度。

( 1 ) I=0;

while (I

I++; //语句3

return(I);

( 2 ) n为不小于1的整数(设k的初值等于1)

void pp ( int k)

{

if (k==n) //语句1

for (I=0; I

printf(a[I]); //语句3

else

{ for (I=k-1;I

a[I]=a[I]+I; //语句5

pp(k+1); //语句6

}

}//pp

(1)这个算法完成在一维数组a[n]中查找给定值k的功能。语句三的频度不仅与问题的规模n有关,还与输入实例中a的各元素取值以及k的取值相关,即与输入实例的初始状态复杂有关。若a中没有与k相等的元素,则语句三的频度为n;若a中的第一个元素a[0]等于k,则语句三的频度是常数0。在这种情况下,可用最坏情况下的时间复杂度作为时间复杂度。在此例中即为O(n)。这样做的原因是:最坏情况下的时间复杂度是在任何输入实例上运行时间的上界。

有时,也可能选择将算法的平均(或期望)时间复杂度作为讨论目标。所谓的平均时间复杂度是指所有可能的输入实例以等概率出现的情况下算法的期望运行时间与问题规模的数量级的关系。此例中,以k出现在任何位置的概率相同,都为1/n,则语句三的执行频度为

[0+1+2+…+(n-1)]/n=(n-1)/2。它决定了此程序段的平均时间复杂度的数量级为f(n)=n,记作O(n)。

(2)在计算包含调用语句的算法的语句频度时,需考虑到调用发生时在被调用算法中各语句的执行情况。本题所示的递归调用较之非递归调用的分析更为复杂。

由于k等于n是算法的出口条件,不妨首先分析算法pp(n)的简单情况,这时各语句的执行频度分别为:1,n+1,n,0,0,0; 而当k=n-1,n-2,…,1时,语句的执行情况和调度情况,如下表所示。

对于k=1即pp(1)而言,各语句的执行次数还须将调用pp(2)时的执行次数累计到内,pp(2)各语句的执行次数又须将调用pp(3)时执行次数累计到内,……由此可的语句频度如下:

语句1:1+1+…+1=n

语句2:0+0+…+0+(n+1)=n+1

语句3:0+0+…+0+n=n

语句4:(n+1)+n+…+3=(n-1)(n+4)/2

语句5:n+(n-1)+…+2=(n-1)(n+2)/2

语句6:1+1+….+1+0=n-1

算法的时间复杂度可以基于频度最大的语句,应为O(n2)。

三、算法的时间复杂度仅与问题的规模相关吗?

不,事实上,算法的时间复杂度不仅与问题的规模相关,还与输入实例中的元素取值等相关,但在最坏的情况下,其时间复杂度就是只与求解问题的规模相关的。我们在讨论时间复杂度时,一般就是以最坏情况下的时间复杂度为准的。

四、

常用的存储表示方法有哪几种?

常用的存储表示方法有四种:

顺序存储方法:它是把逻辑上相邻的结点存储在物理位置相邻的存储单元里,结点间的逻辑关系由存储单元的邻接关系来体现。由此得到的存储表示称为顺序存储结构。

链接存储方法:它不要求逻辑上相邻的结点在物理位置上亦相邻,结点间的逻辑关系是由附加的指针字段表示的。由此得到的存储表示称为链式存储结构。

索引存储方法:除建立存储结点信息外,还建立附加的索引表来标识结点的地址。

散列存储方法:就是根据结点的关键字直接计算出该结点的存储地址。

五、

确定下列算法中输出语句的执行次数,并给出算法的时间复杂度。

(1)void combi (int n)

{ int I,j,k;

for ( I=1; I<=n; I++)

for ( j=I+1; j<=n; j++)

for ( k=j+1; k<=n; k++)

cout<

(2) void binary ( int n)

{ while(n){

cout<

n=n/2;

}}

(1)I取值范围从1~n,j取值范围从I+1~n,k取值范围从j+1~n,情况如下表所示:

所以,输出语句共执行次数为((n-2)+(n-3)+…+1)+((n-3)+(n-4)+…+1)+…+1

= (n-1)(n-2)/2+(n-2)(n-3)/2+…+1

= (((n-1)2+(n-2)2+(n-3)2+…+12)-((n-1)+(n-2)+(n-3)+….+1))/2

=((n-1)n(2n-1)/6-n(n-1)/2)/2

=(n(n-1)(2n-4))/12=n(n-1)(n-2)/6

(2) ceil(log2n);

六、

为什么在单循环链表中设置尾指针比设置头指针更好?

答:尾指针是指向终端结点的指针,用它来表示单循环链表可以使得查找链表的开始结点和终端结点都很方便,设一带头结点的单循环链表,其尾指针为rear,则开始结点和终端结点的位置分别是rear->next->next 和rear, 查找时间都是O(1)。若用头指针来表示该链表,则查找终端结点的时间为O(n)。

七、

何时选用顺序表、何时选用链表作为线性表的存储结构为宜?

答:在实际应用中,应根据具体问题的要求和性质来选择顺序表或链表作为线性表的存储结构,通常有以下几方面的考虑:

1.基于空间的考虑。当要求存储的线性表长度变化不大,易于事先确定其大小时,为了节约存储空间,宜采用顺序表;反之,当线性表长度变化大,难以估计其存储规模时,采用动态链表作为存储结构为好。

2.基于时间的考虑。若线性表的操作主要是进行查找,很少做插入和删除操作时,采用顺序表做存储结构为宜;反之,若需要对线性表进行频繁地插入或删除等的操作时,宜采用链表做存储结构。并且,若链表的插入和删除主要发生在表的首尾两端,则采用尾指针表示的单循环链表为宜。

八、

指出以下算法中的错误和低效之处,并把它改写为一个既正确又高效的算法。

Status DeleteK( SqList &a,int I, int k){ //本过程从顺序存储结构的线性表a中删除第I个元素起的k个元素。

if (I<1|| k<0|| I+k > a.length) return ERROR;

else {

for (count=1;count

for (j=a.Length; j >=I+1;j--) a.elem[j-1] = a.elem[j];

a.length--;

}

rreturn OK;

}//DeleteK

更正:for (j = I+k; j <=a.Length; j++) a.elem[j-k] = a.elem[j];

a.Length = a.Length – k;

九、

假设某个单向循环链表的长度大于1,且表中既无头结点也无头指针。已知s为指向链表中某个结点指针,试编写算法在链表中删除指针s所指结点的前驱结点。

void Delprior ( Link s){

p = q = s;

while (p->next!=s){

q = p;

p = p->next;

}

相关文档
最新文档