(0012)《数据结构》复习思考题答案一、常用的存储表示方法有哪几种?常用的存储表示方法有四种:顺序存储方法:它是把逻辑上相邻的结点存储在物理位置相邻的存储单元里,结点间的逻辑关系由存储单元的邻接关系来体现。
由此得到的存储表示称为顺序存储结构。
链接存储方法:它不要求逻辑上相邻的结点在物理位置上亦相邻,结点间的逻辑关系是由附加的指针字段表示的。
由此得到的存储表示称为链式存储结构。
索引存储方法:除建立存储结点信息外,还建立附加的索引表来标识结点的地址。
散列存储方法:就是根据结点的关键字直接计算出该结点的存储地址。
二、下列程序段带标号语句的频度和时间复杂度。
( 1 ) I=0;while (I<n)&&(a[I]!=k)I++; //语句3return(I);( 2 ) n为不小于1的整数(设k的初值等于1)void pp ( int k){if (k==n) //语句1for (I=0; I<n; I++) //语句2printf(a[I]); //语句3else{ for (I=k-1;I<n;I++) //语句4a[I]=a[I]+I; //语句5pp(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<<I,j,k;}(2) void binary ( int n){ while(n){cout<<n;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<k;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;}q->next = s;delete ( p);}十、假设某个单向循环链表的长度大于1,且表中既无头结点也无头指针。
已知s为指向链表中某个结点指针,试编写算法在链表中删除指针s所指结点的前驱结点。
void Delprior ( Link s){p = q = s;while (p->next!=s){q = p;p = p->next;}q->next = s;delete ( p);}十一、已知L1和L2分别指向两个单链表的头结点,且已知其长度分别为m和n。
试写一算法将这两个链表连接在一起,请分析你的算法的时间复杂度算法如下:LinkList Link( LinkList L1 , LinkList L2 ){//将两个单链表连接在一起ListNode *p , *q ;p=L1;q=L2;while ( p->next ) p=p->next; //查找终端结点p->next = q->next ; //将L2的开始结点链接在L1之后return L1 ;}本算法的主要操作时间花费在查找L1的终端结点上,与L2的长度无关,所以本算的法时间复杂度为:m+1=O(m)十二、已知单链表L是一个递增有序表,试写一高效算法,删除表中值大于min 且小于max的结点(若表中有这样的结点),同时释放被删结点的空间,这里min 和max是两个给定的参数。
请分析你的算法的时间复杂度。
要解这样的问题,我们首先想到的是拿链表中的元素一个个地与max和min比较,然后删除这个结点,其实因为已知其是有序链表,所以我们只要找到大于min的结点的直接前趋结点,再找到小于max的结点,然后一并把中间的全部摘掉就可以了。
算法如下:void DeleteList ( LinkList L, DataType min , DataType max ){ListNode *p , *q ,*r;p=L->next;while( p && p->data <=min ){ //找比min大的前一个元素位置q=p;p=p->next;}while( p && p->data < max ) //找比max小的最后一个元素位置{r=p;p=p->next;free?;//释放这个结点空间}q->next=p; //把断点链上}十三、若以1234作为双端队列的输入序列,试分别求出满足以下条件的输出序列:(1)能由输入受限的双端队列得到,但不能由输出受限的双端队列得到的输出序列;(2)能由输出受限的双端队列得到,但不能由输入受限的双端队列得到的输出序列;(3)既不能由输入受限的双端队列得到,也不能由输出受限的双端队列得到的输出序列。
答:(1)4132;(2)4213;(3)4231。
十四、设两个栈共享空间v[0..m-1],两栈的栈底分别设在向量的两端,且每个元素占一个分量。