
严蔚敏数据结构
-
2023年3月19日发(作者:化工设备机械基础)第1章绪论
1.1简述下列术语:数据,数据元素、数据对象、数据结构、存储结构、数据类
型和抽象数据类型。
解:数据是对客观事物的符号表示。在计算机科学中是指所有能输入到计算机中
并被计算机程序处理的符号的总称。
数据元素是数据的基本单位,在计算机程序中通常作为一个整体进行考虑和
处理。
数据对象是性质相同的数据元素的集合,是数据的一个子集。
数据结构是相互之间存在一种或多种特定关系的数据元素的集合。
存储结构是数据结构在计算机中的表示。
数据类型是一个值的集合和定义在这个值集上的一组操作的总称。
抽象数据类型是指一个数学模型以及定义在该模型上的一组操作。是对一般
数据类型的扩展。
1.2试描述数据结构和抽象数据类型的概念与程序设计语言中数据类型概念的
区别。
解:抽象数据类型包含一般数据类型的概念,但含义比一般数据类型更广、
更抽象。一般数据类型由具体语言系统内部定义,直接提供给编程者定义用户数
据,因此称它们为预定义数据类型。抽象数据类型通常由编程者定义,包括定义
它所使用的数据和在这些数据上所进行的操作。在定义抽象数据类型中的数据部
分和操作部分时,要求只定义到数据的逻辑结构和操作说明,不考虑数据的存储
结构和操作的具体实现,这样抽象层次更高,更能为其他用户提供良好的使用接
口。
1.3设有数据结构(D,R),其中
4,3,2,1ddddD,rR,4,3,3,2,2,1ddddddr
试按图论中图的画法惯例画出其逻辑结构图。
解:
1.4试仿照三元组的抽象数据类型分别写出抽象数据类型复数和有理数的定义
(有理数是其分子、分母均为自然数且分母不为零的分数)。
解:
ADTComplex{
数据对象:D={r,i|r,i为实数}
数据关系:R={}
基本操作:
InitComplex(&C,re,im)
操作结果:构造一个复数C,其实部和虚部分别为re和im
DestroyCmoplex(&C)
操作结果:销毁复数C
Get(C,k,&e)
操作结果:用e返回复数C的第k元的值
Put(&C,k,e)
操作结果:改变复数C的第k元的值为e
IsAscending(C)
操作结果:如果复数C的两个元素按升序排列,则返回1,否则
返回0
IsDescending(C)
操作结果:如果复数C的两个元素按降序排列,则返回1,否则
返回0
Max(C,&e)
操作结果:用e返回复数C的两个元素中值较大的一个
Min(C,&e)
操作结果:用e返回复数C的两个元素中值较小的一个
}ADTComplex
ADTRationalNumber{
数据对象:D={s,m|s,m为自然数,且m不为0}
数据关系:R={}
基本操作:
InitRationalNumber(&R,s,m)
操作结果:构造一个有理数R,其分子和分母分别为s和m
DestroyRationalNumber(&R)
操作结果:销毁有理数R
Get(R,k,&e)
操作结果:用e返回有理数R的第k元的值
Put(&R,k,e)
操作结果:改变有理数R的第k元的值为e
IsAscending(R)
操作结果:若有理数R的两个元素按升序排列,则返回1,否则
返回0
IsDescending(R)
操作结果:若有理数R的两个元素按降序排列,则返回1,否则
返回0
Max(R,&e)
操作结果:用e返回有理数R的两个元素中值较大的一个
Min(R,&e)
操作结果:用e返回有理数R的两个元素中值较小的一个
}ADTRationalNumber
1.5试画出与下列程序段等价的框图。
(1)product=1;i=1;
while(i<=n){
product*=i;
i++;
}
(2)i=0;
do{
i++;
}while((i!=n)&&(a[i]!=x));
(3)switch{
casex casex=y:z=abs(x*y);break; default:z=(x-y)/abs(x)*abs(y); } 1.6在程序设计中,常用下列三种不同的出错处理方式: (1)用exit语句终止执行并报告错误; (2)以函数的返回值区别正确返回或错误返回; (3)设置一个整型变量的函数参数以区别正确返回或某种错误返回。 试讨论这三种方法各自的优缺点。 解:(1)exit常用于异常错误处理,它可以强行中断程序的执行,返回操作 系统。 (2)以函数的返回值判断正确与否常用于子程序的测试,便于实现程序的局 部控制。 (3)用整型函数进行错误处理的优点是可以给出错误类型,便于迅速确定错 误。 1.7在程序设计中,可采用下列三种方法实现输出和输入: (1)通过scanf和printf语句; (2)通过函数的参数显式传递; (3)通过全局变量隐式传递。 试讨论这三种方法的优缺点。 解:(1)用scanf和printf直接进行输入输出的好处是形象、直观,但缺点 是需要对其进行格式控制,较为烦琐,如果出现错误,则会引起整个系统的崩溃。 (2)通过函数的参数传递进行输入输出,便于实现信息的隐蔽,减少出错的 可能。 (3)通过全局变量的隐式传递进行输入输出最为方便,只需修改变量的值即 可,但过多的全局变量使程序的维护较为困难。 1.8设n为正整数。试确定下列各程序段中前置以记号@的语句的频度: (1)i=1;k=0; while(i<=n-1){ @k+=10*i; i++; } (2)i=1;k=0; do{ @k+=10*i; i++; }while(i<=n-1); (3)i=1;k=0; while(i<=n-1){ i++; @k+=10*i; } (4)k=0; for(i=1;i<=n;i++){ for(j=i;j<=n;j++) @k++; } (5)for(i=1;i<=n;i++){ for(j=1;j<=i;j++){ for(k=1;k<=j;k++) @x+=delta; } (6)i=1;j=0; while(i+j<=n){ @if(i>j)j++; elsei++; } (7)x=n;y=0;//n是不小于1的常数 while(x>=(y+1)*(y+1)){ @y++; } (8)x=91;y=100; while(y>0){ @if(x>100){x-=10;y--;} elsex++; } 解:(1)n-1 (2)n-1 (3)n-1 (4)n+(n-1)+(n-2)+...+1= 2 )1(nn (5)1+(1+2)+(1+2+3)+...+(1+2+3+...+n)= n i ii 1 2 )1( = n i n i n i n i iiiiii 11 2 1 2 1 2 1 2 1 )( 2 1 )1( 2 1 = )32)(1( 12 1 )1( 4 1 )12)(1( 12 1 nnnnnnnn (6)n (7)n向下取整 (8)1100 1.9假设n为2的乘幂,并且n>2,试求下列算法的时间复杂度及变量count的 值(以n的函数形式表示)。 intTime(intn){ count=0;x=2; while(x x*=2;count++; } returncount; } 解:)(log 2 no count=2log 2 n 1.11已知有实现同一功能的两个算法,其时间复杂度分别为nO2和10nO,假 设现实计算机可连续运算的时间为710秒(100多天),又每秒可执行基本操作 (根据这些操作来估算算法时间复杂度)510次。试问在此条件下,这两个算法 可解问题的规模(即n值的范围)各为多少?哪个算法更适宜?请说明理由。 解:12102nn=40 121010nn=16 则对于同样的循环次数n,在这个规模下,第二种算法所花费的代价要大得 多。故在这个规模下,第一种算法更适宜。 1.12设有以下三个函数: 10002124nnnf,3450015nnng,nnnnhlog5005.3 请判断以下断言正确与否: (1)f(n)是O(g(n)) (2)h(n)是O(f(n)) (3)g(n)是O(h(n)) (4)h(n)是O(n3.5) (5)h(n)是O(nlogn) 解:(1)对(2)错(3)错(4)对(5)错 1.13试设定若干n值,比较两函数2n和nn 2 log50的增长趋势,并确定n在什么 范围内,函数2n的值大于nn 2 log50的值。 解:2n的增长趋势快。但在n较小的时候,nn 2 log50的值较大。 当n>438时,nnn 2 2log50 1.14判断下列各对函数nf和ng,当n时,哪个函数增长更快? (1)310!ln102nnnnf,724nnng (2)25!lnnnf,5.213nng (3)141.2nnnf,nnng2!ln (4) 2223nnnf,52nnngn 解:(1)g(n)快(2)g(n)快(3)f(n)快(4)f(n)快 1.15试用数学归纳法证明: (1)6/121 1 2 nnni n i 0n (2)1/11 0 xxxn n i i0,1nx (3)122 1 1 n n i i1n (4)2 1 12ni n i 1n 1.16试写一算法,自大至小依次输出顺序读入的三个整数X,Y和Z的值 解: intmax3(intx,inty,intz) { if(x>y) if(x>z)returnx; elsereturnz; else if(y>z)returny; elsereturnz; } 1.17已知k阶斐波那契序列的定义为 0 0 f,0 1 f,…,0 2 k f,1 1 k f; knnnn ffff 21 ,,1,kkn 试编写求k阶斐波那契序列的第m项值的函数算法,k和m均以值调用的形 式在函数参数表中出现。 解:k>0为阶数,n为数列的第n项 intFibonacci(intk,intn) { if(k<1)exit(OVERFLOW); int*p,x; p=newint[k+1]; if(!p)exit(OVERFLOW); inti,j; for(i=0;i if(i elsep[i]=1; } for(i=k+1;i x=p[0]; for(j=0;j p[k]=2*p[k-1]-x; } returnp[k]; } 1.18假设有A,B,C,D,E五个高等院校进行田径对抗赛,各院校的单项成绩 均已存入计算机,并构成一张表,表中每一行的形式为 项目名称性别校名成绩得分 编写算法,处理上述表格,以统计各院校的男、女总分和团体总分,并输出。 解: typedefenum{A,B,C,D,E}SchoolName; typedefenum{Female,Male}SexType; typedefstruct{ charevent[3];//项目 SexTypesex; SchoolNameschool; intscore; }Component; typedefstruct{ intMaleSum;//男团总分 intFemaleSum;//女团总分 intTotalSum;//团体总分 }Sum; SumSumScore(SchoolNamesn,Componenta[],intn) { Sumtemp; m=0; Sum=0; um=0; inti; for(i=0;i if(a[i].school==sn){ if(a[i].sex==Male)m+=a[i].score; if(a[i].sex==Female)Sum+=a[i].score; } } um=m+Sum; returntemp; } 1.19试编写算法,计算ii2!*的值并存入数组a[0..arrsize-1]的第i-1个分量 中(i=1,2,…,n)。假设计算机中允许的整数最大值为maxint,则当n>arrsize 或对某个nkk1,使intmax2!•kk时,应按出错处理。注意选择你认为较 好的出错处理方法。 解: #include #include #defineMAXINT65535 #defineArrSize100 intfun(inti); intmain() { inti,k; inta[ArrSize]; cout<<"Enterk:"; cin>>k; if(k>ArrSize-1)exit(0); for(i=0;i<=k;i++){ if(i==0)a[i]=1; else{ if(2*i*a[i-1]>MAXINT)exit(0); elsea[i]=2*i*a[i-1]; } } for(i=0;i<=k;i++){ if(a[i]>MAXINT)exit(0); elsecout< } return0; } 1.20试编写算法求一元多项式的值 n i i in xaxP 0 的值 0 xP n ,并确定算法中每 一语句的执行次数和整个算法的时间复杂度。注意选择你认为较好的输入和输出 方法。本题的输入为nia i ,,1,0, 0 x和n,输出为 0 xP n 。 解: #include #include #defineN10 doublepolynomail(inta[],inti,doublex,intn); intmain() { doublex; intn,i; inta[N]; cout<<"输入变量的值x:"; cin>>x; cout<<"输入多项式的阶次n:"; cin>>n; if(n>N-1)exit(0); cout<<"输入多项式的系数a[0]--a[n]:"; for(i=0;i>a[i]; cout<<"Thepolynomailvalueis"< return0; } doublepolynomail(inta[],inti,doublex,intn) { if(i>0)returna[n-i]+polynomail(a,i-1,x,n)*x; elsereturna[n]; } 本算法的时间复杂度为o(n)。 第2章线性表 2.1描述以下三个概念的区别:头指针,头结点,首元结点(第一个元素结点)。 解:头指针是指向链表中第一个结点的指针。首元结点是指链表中存储第一 个数据元素的结点。头结点是在首元结点之前附设的一个结点,该结点不存储数 据元素,其指针域指向首元结点,其作用主要是为了方便对链表的操作。它可以 对空表、非空表以及首元结点的操作进行统一处理。 2.2填空题。 解:(1)在顺序表中插入或删除一个元素,需要平均移动表中一半元素,具 体移动的元素个数与元素在表中的位置有关。 (2)顺序表中逻辑上相邻的元素的物理位置必定紧邻。单链表中逻辑上相邻 的元素的物理位置不一定紧邻。 (3)在单链表中,除了首元结点外,任一结点的存储位置由其前驱结点的链 域的值指示。 (4)在单链表中设置头结点的作用是插入和删除首元结点时不用进行特殊 处理。 2.3在什么情况下用顺序表比链表好? 解:当线性表的数据元素在物理位置上是连续存储的时候,用顺序表比用链 表好,其特点是可以进行随机存取。 2.4对以下单链表分别执行下列各程序段,并画出结果示意图。 解: 2.5画出执行下列各行语句后各指针及链表的示意图。 L=(LinkList)malloc(sizeof(LNode));P=L; for(i=1;i<=4;i++){ P->next=(LinkList)malloc(sizeof(LNode)); P=P->next;P->data=i*2-1; } P->next=NULL; for(i=4;i>=1;i--)Ins_LinkList(L,i+1,i*2); for(i=1;i<=3;i++)Del_LinkList(L,i); 解: 2.6已知L是无表头结点的单链表,且P结点既不是首元结点,也不是尾元结点, 试从下列提供的答案中选择合适的语句序列。 a.在P结点后插入S结点的语句序列是__________________。 b.在P结点前插入S结点的语句序列是__________________。 c.在表首插入S结点的语句序列是__________________。 d.在表尾插入S结点的语句序列是__________________。 (1)P->next=S; (2)P->next=P->next->next; (3)P->next=S->next; (4)S->next=P->next; (5)S->next=L; (6)S->next=NULL; (7)Q=P; (8)while(P->next!=Q)P=P->next; (9)while(P->next!=NULL)P=P->next; (10)P=Q; (11)P=L; (12)L=S; (13)L=P; 解:a.(4)(1) b.(7)(11)(8)(4)(1) c.(5)(12) d.(9)(1)(6) 2.7已知L是带表头结点的非空单链表,且P结点既不是首元结点,也不是尾元 结点,试从下列提供的答案中选择合适的语句序列。 a.删除P结点的直接后继结点的语句序列是____________________。 b.删除P结点的直接前驱结点的语句序列是____________________。 c.删除P结点的语句序列是____________________。 d.删除首元结点的语句序列是____________________。 e.删除尾元结点的语句序列是____________________。 (1)P=P->next; (2)P->next=P; (3)P->next=P->next->next; (4)P=P->next->next; (5)while(P!=NULL)P=P->next; (6)while(Q->next!=NULL){P=Q;Q=Q->next;} (7)while(P->next!=Q)P=P->next; (8)while(P->next->next!=Q)P=P->next; (9)while(P->next->next!=NULL)P=P->next; (10)Q=P; (11)Q=P->next; (12)P=L; (13)L=L->next; (14)free(Q); 解:a.(11)(3)(14) b.(10)(12)(8)(3)(14) c.(10)(12)(7)(3)(14) d.(12)(11)(3)(14) e.(9)(11)(3)(14) 2.8已知P结点是某双向链表的中间结点,试从下列提供的答案中选择合适的语 句序列。 a.在P结点后插入S结点的语句序列是_______________________。 b.在P结点前插入S结点的语句序列是_______________________。 c.删除P结点的直接后继结点的语句序列是_______________________。 d.删除P结点的直接前驱结点的语句序列是_______________________。 e.删除P结点的语句序列是_______________________。 (1)P->next=P->next->next; (2)P->priou=P->priou->priou; (3)P->next=S; (4)P->priou=S; (5)S->next=P; (6)S->priou=P; (7)S->next=P->next; (8)S->priou=P->priou; (9)P->priou->next=P->next; (10)P->priou->next=P; (11)P->next->priou=P; (12)P->next->priou=S; (13)P->priou->next=S; (14)P->next->priou=P->priou; (15)Q=P->next; (16)Q=P->priou; (17)free(P); (18)free(Q); 解:a.(7)(3)(6)(12) b.(8)(4)(5)(13) c.(15)(1)(11)(18) d.(16)(2)(10)(18) e.(14)(9)(17) 2.9简述以下算法的功能。 (1)StatusA(LinkedListL){//L是无表头结点的单链表 if(L&&L->next){ Q=L;L=L->next;P=L; while(P->next)P=P->next; P->next=Q;Q->next=NULL; } returnOK; } (2)voidBB(LNode*s,LNode*q){ p=s; while(p->next!=q)p=p->next; p->next=s; } voidAA(LNode*pa,LNode*pb){ //pa和pb分别指向单循环链表中的两个结点 BB(pa,pb); BB(pb,pa); } 解:(1)如果L的长度不小于2,将L的首元结点变成尾元结点。 (2)将单循环链表拆成两个单循环链表。 2.10指出以下算法中的错误和低效之处,并将它改写为一个既正确又高效的算 法。 StatusDeleteK(SqList&a,inti,intk) { //本过程从顺序存储结构的线性表a中删除第i个元素起的k个元素 if(i<1||k)returnINFEASIBLE;//参数不合法 else{ for(count=1;count //删除第一个元素 for(j=;j>=i+1;j--)[j-i]=[j]; --; } returnOK; } 解: StatusDeleteK(SqList&a,inti,intk) { //从顺序存储结构的线性表a中删除第i个元素起的k个元素 //注意i的编号从0开始 intj; if(i-1||k-i)returnINFEASIBLE; for(j=0;j<=k;j++) [j+i]=[j+i+k]; =-k; returnOK; } 2.11设顺序表va中的数据元素递增有序。试写一算法,将x插入到顺序表的适 当位置上,以保持该表的有序性。 解: StatusInsertOrderList(SqList&va,ElemTypex) { //在非递减的顺序表va中插入元素x并使其仍成为顺序表的算法 inti; if(==ze)return(OVERFLOW); for(i=;i>0,x<[i-1];i--) [i]=[i-1]; [i]=x; ++; returnOK; } 2.12设 m aaA,, 1 和 n bbB,, 1 均为顺序表,A 和B 分别为A和B中除 去最大共同前缀后的子表。若 BA空表,则BA;若A =空表,而 B空 表,或者两者均不为空表,且A 的首元小于B 的首元,则BA;否则BA。 试写一个比较A,B大小的算法。 解: StatusCompareOrderList(SqList&A,SqList&B) { inti,k,j; k=>?:; for(i=0;i if([i]>[i])j=1; if([i]<[i])j=-1; } if(>k)j=1; if(>k)j=-1; if(==)j=0; returnj; } 2.13试写一算法在带头结点的单链表结构上实现线性表操作Locate(L,x); 解: intLocateElem_L(LinkList&L,ElemTypex) { inti=0; LinkListp=L; while(p&&p->data!=x){ p=p->next; i++; } if(!p)return0; elsereturni; } 2.14试写一算法在带头结点的单链表结构上实现线性表操作Length(L)。 解: //返回单链表的长度 intListLength_L(LinkList&L) { inti=0; LinkListp=L; if(p)p=p-next; while(p){ p=p->next; i++; } returni; } 2.15已知指针ha和hb分别指向两个单链表的头结点,并且已知两个链表的长 度分别为m和n。试写一算法将这两个链表连接在一起,假设指针hc指向连接 后的链表的头结点,并要求算法以尽可能短的时间完成连接运算。请分析你的算 法的时间复杂度。 解: voidMergeList_L(LinkList&ha,LinkList&hb,LinkList&hc) { LinkListpa,pb; pa=ha; pb=hb; while(pa->next&&pb->next){ pa=pa->next; pb=pb->next; } if(!pa->next){ hc=hb; while(pb->next)pb=pb->next; pb->next=ha->next; } else{ hc=ha; while(pa->next)pa=pa->next; pa->next=hb->next; } } 2.16已知指针la和lb分别指向两个无头结点单链表中的首元结点。下列算法 是从表la中删除自第i个元素起共len个元素后,将它们插入到表lb中第i 个元素之前。试问此算法是否正确?若有错,请改正之。 StatusDeleteAndInsertSub(LinkedListla,LinkedListlb,inti,int j,intlen) { if(i<0||j<0||len<0)returnINFEASIBLE; p=la;k=1; while(knext;k++;} q=p; while(knext;k++;} s=lb;k=1; while(knext;k++;} s->next=p;q->next=s->next; returnOK; } 解: StatusDeleteAndInsertSub(LinkList&la,LinkList&lb,inti,intj,int len) { LinkListp,q,s,prev=NULL; intk=1; if(i<0||j<0||len<0)returnINFEASIBLE; //在la表中查找第i个结点 p=la; while(p&&k prev=p; p=p->next; k++; } if(!p)returnINFEASIBLE; //在la表中查找第i+len-1个结点 q=p;k=1; while(q&&k q=p->next; k++; } if(!q)returnINFEASIBLE; //完成删除,注意,i=1的情况需要特殊处理 if(!prev)la=q->next; elseprev->next=q->next; //将从la中删除的结点插入到lb中 if(j=1){ q->next=lb; lb=p; } else{ s=lb;k=1; while(s&&k s=s->next; k++; } if(!s)returnINFEASIBLE; q->next=s->next; s->next=p;//完成插入 } returnOK; } 2.17试写一算法,在无头结点的动态单链表上实现线性表操作Insert(L,i,b), 并和在带头结点的动态单链表上实现相同操作的算法进行比较。 2.18试写一算法,实现线性表操作Delete(L,i),并和在带头结点的动态单链表 上实现相同操作的算法进行比较。 2.19已知线性表中的元素以值递增有序排列,并以单链表作存储结构。试写一 高效的算法,删除表中所有值大于mink且小于maxk的元素(若表中存在这样的 元素),同时释放被删结点空间,并分析你的算法的时间复杂度(注意,mink 和maxk是给定的两个参变量,它们的值可以和表中的元素相同,也可以不同)。 解: StatusListDelete_L(LinkList&L,ElemTypemink,ElemTypemaxk) { LinkListp,q,prev=NULL; if(mink>maxk)returnERROR; p=L; prev=p; p=p->next; while(p&&p->data if(p->data<=mink){ prev=p; p=p->next; } else{ prev->next=p->next; q=p; p=p->next; free(q); } } returnOK; } 2.20同2.19题条件,试写一高效的算法,删除表中所有值相同的多余元素(使 得操作后的线性表中所有元素的值均不相同),同时释放被删结点空间,并分析 你的算法的时间复杂度。 解: voidListDelete_LSameNode(LinkList&L) { LinkListp,q,prev; p=L; prev=p; p=p->next; while(p){ prev=p; p=p->next; if(p&&p->data==prev->data){ prev->next=p->next; q=p; p=p->next; free(q); } } } 2.21试写一算法,实现顺序表的就地逆置,即利用原表的存储空间将线性表 n aa,, 1 逆置为 1 ,,aa n 。 解: //顺序表的逆置 StatusListOppose_Sq(SqList&L) { inti; ElemTypex; for(i=0;i2;i++){ x=[i]; [i]=[-1-i]; [-1-i]=x; } returnOK; } 2.22试写一算法,对单链表实现就地逆置。 解: //带头结点的单链表的逆置 StatusListOppose_L(LinkList&L) { LinkListp,q; p=L; p=p->next; L->next=NULL; while(p){ q=p; p=p->next; q->next=L->next; L->next=q; } returnOK; } 2.23设线性表 m aaaA,,, 21 , n bbbB,,, 21 ,试写一个按下列规则合并 A,B为线性表C的算法,即使得 nmmm bbbabaC,,,,,,, 111 当nm时; mnnn aababaC,,,,,,, 111 当nm时。 线性表A,B和C均以单链表作存储结构,且C表利用A表和B表中的结点空间 构成。注意:单链表的长度值m和n均未显式存储。 解: //将合并后的结果放在C表中,并删除B表 StatusListMerge_L(LinkList&A,LinkList&B,LinkList&C) { LinkListpa,pb,qa,qb; pa=A->next; pb=B->next; C=A; while(pa&&pb){ qa=pa;qb=pb; pa=pa->next;pb=pb->next; qb->next=qa->next; qa->next=qb; } if(!pa)qb->next=pb; pb=B; free(pb); returnOK; } 2.24假设有两个按元素值递增有序排列的线性表A和B,均以单链表作存储结 构,请编写算法将A表和B表归并成一个按元素值递减有序(即非递增有序,允 许表中含有值相同的元素)排列的线性表C,并要求利用原表(即A表和B表) 的结点空间构造C表。 解: //将合并逆置后的结果放在C表中,并删除B表 StatusListMergeOppose_L(LinkList&A,LinkList&B,LinkList&C) { LinkListpa,pb,qa,qb; pa=A; pb=B; qa=pa;//保存pa的前驱指针 qb=pb;//保存pb的前驱指针 pa=pa->next; pb=pb->next; A->next=NULL; C=A; while(pa&&pb){ if(pa->datadata){ qa=pa; pa=pa->next; qa->next=A->next;//将当前最小结点插入A表表头 A->next=qa; } else{ qb=pb; pb=pb->next; qb->next=A->next;//将当前最小结点插入A表表头 A->next=qb; } } while(pa){ qa=pa; pa=pa->next; qa->next=A->next; A->next=qa; } while(pb){ qb=pb; pb=pb->next; qb->next=A->next; A->next=qb; } pb=B; free(pb); returnOK; } 2.25假设以两个元素依值递增有序排列的线性表A和B分别表示两个集合(即 同一表中的元素值各不相同),现要求另辟空间构成一个线性表C,其元素为A 和B中元素的交集,且表C中的元素有依值递增有序排列。试对顺序表编写求C 的算法。 解: //将A、B求交后的结果放在C表中 StatusListCross_Sq(SqList&A,SqList&B,SqList&C) { inti=0,j=0,k=0; while(i<&&j<){ if([i]<[j])i++; else if([i]>[j])j++; else{ ListInsert_Sq(C,k,[i]); i++; k++; } } returnOK; } 2.26要求同2.25题。试对单链表编写求C的算法。 解: //将A、B求交后的结果放在C表中,并删除B表 StatusListCross_L(LinkList&A,LinkList&B,LinkList&C) { LinkListpa,pb,qa,qb,pt; pa=A; pb=B; qa=pa;//保存pa的前驱指针 qb=pb;//保存pb的前驱指针 pa=pa->next; pb=pb->next; C=A; while(pa&&pb){ if(pa->datadata){ pt=pa; pa=pa->next; qa->next=pa; free(pt); } else if(pa->data>pb->data){ pt=pb; pb=pb->next; qb->next=pb; free(pt); } else{ qa=pa; pa=pa->next; } } while(pa){ pt=pa; pa=pa->next; qa->next=pa; free(pt); } while(pb){ pt=pb; pb=pb->next; qb->next=pb; free(pt); } pb=B; free(pb); returnOK; } 2.27对2.25题的条件作以下两点修改,对顺序表重新编写求得表C的算法。 (1)假设在同一表(A或B)中可能存在值相同的元素,但要求新生成的表C 中的元素值各不相同; (2)利用A表空间存放表C。 解: (1) //A、B求交,然后删除相同元素,将结果放在C表中 StatusListCrossDelSame_Sq(SqList&A,SqList&B,SqList&C) { inti=0,j=0,k=0; while(i<&&j<){ if([i]<[j])i++; else if([i]>[j])j++; else{ if(==0){ ListInsert_Sq(C,k,[i]); k++; } else if([-1]!=[i]){ ListInsert_Sq(C,k,[i]); k++; } i++; } } returnOK; } (2) //A、B求交,然后删除相同元素,将结果放在A表中 StatusListCrossDelSame_Sq(SqList&A,SqList&B) { inti=0,j=0,k=0; while(i<&&j<){ if([i]<[j])i++; else if([i]>[j])j++; else{ if(k==0){ [k]=[i]; k++; } else if([k]!=[i]){ [k]=[i]; k++; } i++; } } =k; returnOK; } 2.28对2.25题的条件作以下两点修改,对单链表重新编写求得表C的算法。 (1)假设在同一表(A或B)中可能存在值相同的元素,但要求新生成的表C 中的元素值各不相同; (2)利用原表(A表或B表)中的结点构成表C,并释放A表中的无用结点 空间。 解: (1) //A、B求交,结果放在C表中,并删除相同元素 StatusListCrossDelSame_L(LinkList&A,LinkList&B,LinkList&C) { LinkListpa,pb,qa,qb,pt; pa=A; pb=B; qa=pa;//保存pa的前驱指针 qb=pb;//保存pb的前驱指针 pa=pa->next; pb=pb->next; C=A; while(pa&&pb){ if(pa->datadata){ pt=pa; pa=pa->next; qa->next=pa; free(pt); } else if(pa->data>pb->data){ pt=pb; pb=pb->next; qb->next=pb; free(pt); } else{ if(pa->data==qa->data){ pt=pa; pa=pa->next; qa->next=pa; free(pt); } else{ qa=pa; pa=pa->next; } } } while(pa){ pt=pa; pa=pa->next; qa->next=pa; free(pt); } while(pb){ pt=pb; pb=pb->next; qb->next=pb; free(pt); } pb=B; free(pb); returnOK; } (2) //A、B求交,结果放在A表中,并删除相同元素 StatusListCrossDelSame_L(LinkList&A,LinkList&B) { LinkListpa,pb,qa,qb,pt; pa=A; pb=B; qa=pa;//保存pa的前驱指针 qb=pb;//保存pb的前驱指针 pa=pa->next; pb=pb->next; while(pa&&pb){ if(pa->datadata){ pt=pa; pa=pa->next; qa->next=pa; free(pt); } else if(pa->data>pb->data){ pt=pb; pb=pb->next; qb->next=pb; free(pt); } else{ if(pa->data==qa->data){ pt=pa; pa=pa->next; qa->next=pa; free(pt); } else{ qa=pa; pa=pa->next; } } } while(pa){ pt=pa; pa=pa->next; qa->next=pa; free(pt); } while(pb){ pt=pb; pb=pb->next; qb->next=pb; free(pt); } pb=B; free(pb); returnOK; } 2.29已知A,B和C为三个递增有序的线性表,现要求对A表作如下操作:删去 那些既在B表中出现又在C表中出现的元素。试对顺序表编写实现上述操作的算 法,并分析你的算法的时间复杂度(注意:题中没有特别指明同一表中的元素值 各不相同)。 解: //在A中删除既在B中出现又在C中出现的元素,结果放在D中 StatusListUnion_Sq(SqList&D,SqList&A,SqList&B,SqList&C) { SqListTemp; InitList_Sq(Temp); ListCross_L(B,C,Temp); ListMinus_L(A,Temp,D); } 2.30要求同2.29题。试对单链表编写算法,请释放A表中的无用结点空间。 解: //在A中删除既在B中出现又在C中出现的元素,并释放B、C StatusListUnion_L(LinkList&A,LinkList&B,LinkList&C) { ListCross_L(B,C); ListMinus_L(A,B); } //求集合A-B,结果放在A表中,并删除B表 StatusListMinus_L(LinkList&A,LinkList&B) { LinkListpa,pb,qa,qb,pt; pa=A; pb=B; qa=pa;//保存pa的前驱指针 qb=pb;//保存pb的前驱指针 pa=pa->next; pb=pb->next; while(pa&&pb){ if(pb->datadata){ pt=pb; pb=pb->next; qb->next=pb; free(pt); } else if(pb->data>pa->data){ qa=pa; pa=pa->next; } else{ pt=pa; pa=pa->next; qa->next=pa; free(pt); } } while(pb){ pt=pb; pb=pb->next; qb->next=pb; free(pt); } pb=B; free(pb); returnOK; } 2.31假设某个单向循环链表的长度大于1,且表中既无头结点也无头指针。已 知s为指向链表中某个结点的指针,试编写算法在链表中删除指针s所指结点的 前驱结点。 解: //在单循环链表S中删除S的前驱结点 StatusListDelete_CL(LinkList&S) { LinkListp,q; if(S==S->next)returnERROR; q=S; p=S->next; while(p->next!=S){ q=p; p=p->next; } q->next=p->next; free(p); returnOK; } 2.32已知有一个单向循环链表,其每个结点中含三个域:pre,data和next, 其中data为数据域,next为指向后继结点的指针域,pre也为指针域,但它的 值为空,试编写算法将此单向循环链表改为双向循环链表,即使pre成为指向前 驱结点的指针域。 解: //建立一个空的循环链表 StatusInitList_DL(DuLinkList&L) { L=(DuLinkList)malloc(sizeof(DuLNode)); if(!L)exit(OVERFLOW); L->pre=NULL; L->next=L; returnOK; } //向循环链表中插入一个结点 StatusListInsert_DL(DuLinkList&L,ElemTypee) { DuLinkListp; p=(DuLinkList)malloc(sizeof(DuLNode)); if(!p)returnERROR; p->data=e; p->next=L->next; L->next=p; returnOK; } //将单循环链表改成双向链表 StatusListCirToDu(DuLinkList&L) { DuLinkListp,q; q=L; p=L->next; while(p!=L){ p->pre=q; q=p; p=p->next; } if(p==L)p->pre=q; returnOK; } 2.33已知由一个线性链表表示的线性表中含有三类字符的数据元素(如:字母 字符、数字字符和其他字符),试编写算法将该线性表分割为三个循环链表,其 中每个循环链表表示的线性表中均只含一类字符。 解: //将单链表L划分成3个单循环链表 StatusListDivideInto3CL(LinkList&L,LinkList&s1,LinkList &s2,LinkList&s3) { LinkListp,q,pt1,pt2,pt3; p=L->next; pt1=s1; pt2=s2; pt3=s3; while(p){ if(p->data>='0'&&p->data<='9'){ q=p; p=p->next; q->next=pt1->next; pt1->next=q; pt1=pt1->next; } else if((p->data>='A'&&p->data<='Z')|| (p->data>='a'&&p->data<='z')){ q=p; p=p->next; q->next=pt2->next; pt2->next=q; pt2=pt2->next; } else{ q=p; p=p->next; q->next=pt3->next; pt3->next=q; pt3=pt3->next; } } q=L; free(q); returnOK; } 在2.34至2.36题中,“异或指针双向链表”类型XorLinkedList和指针异 或函数XorP定义为: typedefstructXorNode{ chardata; structXorNode*LRPtr; }XorNode,*XorPointer; typedestruct{//无头结点的异或指针双向链表 XorPointerLeft,Right;//分别指向链表的左侧和右端 }XorLinkedList; XorPointerXorP(XorPointerp,XorPointerq); //指针异或函数XorP返回指针p和q的异或值 2.34假设在算法描述语言中引入指针的二元运算“异或”,若a和b为指针, 则a⊕b的运算结果仍为原指针类型,且 a⊕(a⊕b)=(a⊕a)⊕b=b (a⊕b)⊕b=a⊕(b⊕b)=a 则可利用一个指针域来实现双向链表L。链表L中的每个结点只含两个域:data 域和LRPtr域,其中LRPtr域存放该结点的左邻与右邻结点指针(不存在时为 NULL)的异或。若设指针指向链表中的最左结点,指向链表中 的最右结点,则可实现从左向右或从右向左遍历此双向链表的操作。试写一算法 按任一方向依次输出链表中各元素的值。 解: StatusTraversingLinkList(XorLinkedList&L,chard) { XorPointerp,left,right; if(d=='l'||d=='L'){ p=; left=NULL; while(p!=NULL){ VisitingData(p->data); left=p; p=XorP(left,p->LRPtr); } } else if(d=='r'||d=='R'){ p=; right=NULL; while(p!=NULL){ VisitingData(p->data); right=p; p=XorP(p->LRPtr,right); } } elsereturnERROR; returnOK; } 2.35采用2.34题所述的存储结构,写出在第i个结点之前插入一个结点的算法。 2.36采用2.34题所述的存储结构,写出删除第i个结点的算法。 2.37设以带头结点的双向循环链表表示的线性表 n aaaL,,, 21 。试写一时间 复杂度O(n)的算法,将L改造为 2431 ,,,,,,aaaaaL n 。 解: //将双向链表L=(a1,a2,...,an)改造为(a1,a3,...,an,...,a2) StatusListChange_DuL(DuLinkList&L) { inti; DuLinkListp,q,r; p=L->next; r=L->pre; i=1; while(p!=r){ if(i%2==0){ q=p; p=p->next; //删除结点 q->pre->next=q->next; q->next->pre=q->pre; //插入到头结点的左面 q->pre=r->next->pre; r->next->pre=q; q->next=r->next; r->next=q; } elsep=p->next; i++; } returnOK; } 2.38设有一个双向循环链表,每个结点中除有pre,data和next三个域外,还 增设了一个访问频度域freq。在链表被起用之前,频度域freq的值均初始化为 零,而每当对链表进行一次Locate(L,x)的操作后,被访问的结点(即元素值等 于x的结点)中的频度域freq的值便增1,同时调整链表中结点之间的次序, 使其按访问频度非递增的次序顺序排列,以便始终保持被频繁访问的结点总是靠 近表头结点。试编写符合上述要求的Locate操作的算法。 解: DuLinkListListLocate_DuL(DuLinkList&L,ElemTypee) { DuLinkListp,q; p=L->next; while(p!=L&&p->data!=e)p=p->next; if(p==L)returnNULL; else{ p->freq++; //删除结点 p->pre->next=p->next; p->next->pre=p->pre; //插入到合适的位置 q=L->next; while(q!=L&&q->freq>p->freq)q=q->next; if(q==L){ p->next=q->next; q->next=p; p->pre=q->pre; q->pre=p; } else{ //在q之前插入 p->next=q->pre->next; q->pre->next=p; p->pre=q->pre; q->pre=p; } returnp; } } 在2.39至2.40题中,稀疏多项式采用的顺序存储结构SqPoly定义为 typedefstruct{ intcoef; intexp; }PolyTerm; typedefstruct{//多项式的顺序存储结构 PolyTerm*data; intlast; }SqPoly; 2.39已知稀疏多项式m e m ee n xcxcxcxP21 21 ,其中 0 11 eeen mm ,mic i ,,2,10, 1m 。试采用存储量同多项式项 数m成正比的顺序存储结构,编写求 0 xP n 的算法( 0 x为给定值),并分析你的 算法的时间复杂度。 解: typedefstruct{ intcoef; intexp; }PolyTerm; typedefstruct{ PolyTerm*data; intlast; }SqPoly; //建立一个多项式 StatusPolyInit(SqPoly&L) { inti; PolyTerm*p; cout<<"请输入多项式的项数:"; cin>>; =(PolyTerm*)malloc(*sizeof(PolyTerm)); if(!)returnERROR; p=; for(i=0;i<;i++){ cout<<"请输入系数:"; cin>>p->coef; cout<<"请输入指数:"; cin>>p->exp; p++; } returnOK; } //求多项式的值 doublePolySum(SqPoly&L,doublex0) { doublePn,x; inti,j; PolyTerm*p; p=; for(i=0,Pn=0;i<;i++,p++){ for(j=0,x=1;jexp;j++)x=x*x0; Pn=Pn+p->coef*x; } returnPn; } 2.40采用2.39题给定的条件和存储结构,编写求xPxPxP nn21 的算法, 将结果多项式存放在新辟的空间中,并分析你的算法的时间复杂度。 解: //求两多项式的差 StatusPolyMinus(SqPoly&L,SqPoly&L1,SqPoly&L2) { PolyTerm*p,*p1,*p2; p=; p1=; p2=; inti=0,j=0,k=0; while(i<&&j<){ if(p1->expexp){ p->coef=p1->coef; p->exp=p1->exp; p++;k++; p1++;i++; } else if(p1->exp>p2->exp){ p->coef=-p2->coef; p->exp=p2->exp; p++;k++; p2++;j++; } else{ if(p1->coef!=p2->coef){ p->coef=(p1->coef)-(p2->coef); p->exp=p1->exp; p++;k++; } p1++;p2++; i++;j++; } } if(i<) while(i<){ p->coef=p1->coef; p->exp=p1->exp; p++;k++; p1++;i++; } if(j<) while(j<){ p->coef=-p2->coef; p->exp=p2->exp; p++;k++; p2++;j++; } =k; returnOK; } 在2.41至2.42题中,稀疏多项式采用的循环链表存储结构LinkedPoly定 义为 typedefstructPolyNode{ PolyTermdata; structPolyNode*next; }PolyNode,*PolyLink; typedefPolyLinkLinkedPoly; 2.41试以循环链表作稀疏多项式的存储结构,编写求其导函数的方法,要求利 用原多项式中的结点空间存放其导函数多项式,同时释放所有无用结点。 解: StatusPolyDifferential(LinkedPoly&L) { LinkedPolyp,q,pt; q=L; p=L->next; while(p!=L){ if(p->==0){ pt=p; p=p->next; q->next=p; free(pt); } else{ p->=p->*p->; p->--; q=p; p=p->next; } } returnOK; } 2.42试编写算法,将一个用循环链表表示的稀疏多项式分解成两个多项式,使 这两个多项式中各自仅含奇次项或偶次项,并要求利用原链表中的结点空间构成 这两个链表。 解: //将单链表L划分成2个单循环链表 StatusListDivideInto2CL(LinkedPoly&L,LinkedPoly&L1) { LinkedPolyp,p1,q,pt; q=L; p=L->next; p1=L1; while(p!=L){ if(p->%2==0){ pt=p; p=p->next; q->next=p; pt->next=p1->next; p1->next=pt; p1=p1->next; } else{ q=p; p=p->next; } } returnOK; } 第3章栈和队列 3.1 (1)如果进站的车厢序列为123,则可能得到的出站车厢序列是什么? (2)如果进站的车厢序列为123456,则能否得到435612和135426的出站序 列,并请说明为什么不能得到或者如何得到(即写出以‘S’表示进栈和以‘X’表 示出栈的栈操作序列)。 解:(1)3132 (2)可以得到135426的出站序列,但不能得到435612的出站序列。因为 4356出站说明12已经在栈中,1不可能先于2出栈。 3.2简述栈和线性表的差别。 解:线性表是具有相同特性的数据元素的一个有限序列。栈是限定仅在表尾 进行插入或删除操作的线性表。 3.3写出下列程序段的输出结果(栈的元素类型SElemType为char)。 voidmain() { StackS; charx,y; InitStack(S); x=‘c’;y=‘k’; Push(S,x);Push(S,‘a’);Push(S,y); Pop(S,x);Push(S,‘t’);Push(S,x); Pop(S,x);Push(S,‘s’); while(!StackEmpty(S)){Pop(S,y);printf(y);} printf(x); } 解:stack 3.4简述以下算法的功能(栈的元素类型SElemType为int)。 (1)statusalgo1(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)statusalgo2(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); } } 解:(1)栈中的数据元素逆置(2)如果栈中存在元素e,将其从栈中清 除 3.5假设以S和X分别表示入栈和出栈的操作,则初态和终态均为空栈的入栈和 出栈的操作序列可以表示为仅由S和X组成的序列。称可以操作的序列为合法序 列(例如,SXSX为合法序列,SXXS为非法序列)。试给出区分给定序列为合法 序列或非法序列的一般准则,并证明:两个不同的合法(栈操作)序列(对同一 输入序列)不可能得到相同的输出元素(注意:在此指的是元素实体,而不是值) 序列。 解:任何前n个序列中S的个数一定大于X的个数。 设两个合法序列为: T1=S……X……S…… T2=S……X……X…… 假定前n个操作都相同,从第n+1个操作开始,为序列不同的起始操作点。 由于前n个操作相同,故此时两个栈(不妨为栈A、B)的存储情况完全相同, 假设此时栈顶元素均为a。 第n+1个操作不同,不妨T1的第n+1个操作为S,T2的第n+1个操作为X。 T1为入栈操作,假设将b压栈,则T1的输出顺序一定是先b后a;而T2将a 退栈,则其输出顺序一定是先a后b。由于T1的输出为……ba……,而T2的输 出顺序为……ab……,说明两个不同的合法栈操作序列的输出元素的序列一定不 同。 3.6试证明:若借助栈由输入序列12…n得到的输出序列为 n ppp 21 (它是输入 序列的一个排列),则在输出序列中不可能出现这样的情形:存在着i j p< k p< i p。 解:这个问题和3.1题比较相似。因为输入序列是从小到大排列的,所以若 j p< k p< i p,则可以理解为通过输入序列 j p k p i p可以得到输出序列 i p j p k p, 显然通过序列123是无法得到312的,参见3.1题。所以不可能存在着i 使 j p< k p< i p。 3.7按照四则运算加、减、乘、除和幂运算(↑)优先关系的惯例,并仿照教科书 3.2节例3-2的格式,画出对下列算术表达式求值时操作数栈和运算符栈的变化 过程: A-B×C/D+E↑F 解:BC=GG/D=HA-H=IE^F=JI+J=K 步 骤 OPTR栈OPND栈输入字符主要操作 1#A-B*C/D+E^F #PUSH(OPND,A) 2#A-B*C/D+E^F#PUSH(OPTR,-) 3#-AB*C/D+E^F#PUSH(OPND,B) 4#-AB*C/D+E^F#PUSH(OPTR,*) 5#-*ABC/D+E^F#PUSH(OPND,C) 6#-*ABC/D+E^F#Operate(B,*,C) 7#-AG/D+E^F#PUSH(OPTR,/) 8#-/AGD+E^F#PUSH(OPND,D) 9#-/AGD+E^F#Operate(G,/,D) 10#-AH+E^F#Operate(A,-,H) 11#I+E^F#PUSH(OPTR,+) 12#+IE^F#PUSH(OPND,E) 13#+IE^F#PUSH(OPTR,^) 14#+^IEF#PUSH(OPND,F) 15#+^IEF#Operate(E,^,F) 16#+IJ#Operate(I,+,J) 17#K#RETURN 3.8试推导求解n阶梵塔问题至少要执行的move操作的次数。 解: 12n 3.9试将下列递推过程改写为递归过程。 voidditui(intn) { inti; i=n; while(i>1) cout< } 解: voidditui(intj) { if(j>1){ cout< ditui(j-1); } return; } 3.10试将下列递归过程改写为非递归过程。 voidtest(int&sum) { intx; cin>>x; if(x==0)sum=0; else { test(sum); sum+=x; } cout< } 解: voidtest(int&sum) { Stacks; InitStack(s); intx; do{ cin>>x; Push(s,x); }while(x>0); while(!StackEmpty(s)){ Pop(s,x); sum+=x; cout< } DestoryStack(s); } 3.11简述队列和堆栈这两种数据类型的相同点和差异处。 解:栈是一种运算受限的线性表,其限制是仅允许在表的一端进行插入和删 除运算。 队列也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,而 在表的另一端进行删除。 3.12写出以下程序段的输出结果(队列中的元素类型QElemType为char)。 voidmain() { QueueQ; InitQueue(Q); charx=‘e’,y=‘c’; EnQueue(Q,‘h’); EnQueue(Q,‘r’); EnQueue(Q,y); DeQueue(Q,x); EnQueue(Q,x); DeQueue(Q,x); EnQueue(Q,‘a’); While(!QueueEmpty(Q)) { DeQueue(Q,y); cout< } cout< } 解:char 3.13简述以下算法的功能(栈和队列的元素类型均为int)。 voidalgo3(Queue&Q) { StackS; intd; InitStack(S); while(!QueueEmpty(Q)) { DeQueue(Q,d); Push(S,d); } while(!StackEmpty(S)) { Pop(S,d); EnQueue(Q,d); } } 解:队列逆置 3.14若以1234作为双端队列的输入序列,试分别求出满足以下条件的输出序列: (1)能由输入受限的双端队列得到,但不能由输出受限的双端队列得到的输 出序列。 (2)能由输出受限的双端队列得到,但不能由输入受限的双端队列得到的输 出序列。 (3)既不能由输入受限的双端队列得到,也不能由输出受限的双端队列得到 的输出序列。 3.15假设以顺序存储结构实现一个双向栈,即在一维数组的存储空间中存在着 两个栈,它们的栈底分别设在数组的两个端点。试编写实现这个双向栈tws的三 个操作:初始化inistack(tws)、入栈push(tws,i,x)和出栈pop(tws,i)的算法, 其中i为0或1,用以分别指示设在数组两端的两个栈,并讨论按过程(正/误状 态变量可设为变参)或函数设计这些操作算法各有什么有缺点。 解: classDStack{ ElemType*top[2]; ElemType*p; intstacksize; intdi; public: DStack(intm) { p=newElemType[m]; if(!p)exit(OVERFLOW); top[0]=p+m/2; top[1]=top[0]; stacksize=m; } ~DStack(){deletep;} voidPush(inti,ElemTypex) { di=i; if(di==0){ if(top[0]>=p)*top[0]--=x; elsecerr<<"Stackoverflow!"; } else{ if(top[1] elsecerr<<"Stackoverflow!"; } } ElemTypePop(inti) { di=i; if(di==0){ if(top[0] elsecerr<<"Stackempty!"; }else{ if(top[1]>top[0])return*top[1]--; elsecerr<<"Stackempty!"; } returnOK; } }; //链栈的数据结构及方法的定义 typedefstructNodeType{ ElemTypedata; NodeType*next; }NodeType,*LinkType; typedefstruct{ LinkTypetop; intsize; }Stack; voidInitStack(Stack&s) { =NULL; =0; } voidDestroyStack(Stack&s) { LinkTypep; while(){ p=; =p->next; deletep; --; } } voidClearStack(Stack&s) { LinkTypep; while(){ p=; =p->next; deletep; --; } } intStackLength(Stacks) { ; } StatusStackEmpty(Stacks) { if(==0)returnTRUE; elsereturnFALSE; } StatusGetTop(Stacks,ElemType&e) { if(!)returnERROR; else{ e=->data; returnOK; } } StatusPush(Stack&s,ElemTypee) { LinkTypep; p=newNodeType; if(!p)exit(OVERFLOW); p->next=; =p; p->data=e; ++; returnOK; } StatusPop(Stack&s,ElemType&e) { LinkTypep; if(){ e=->data; p=; =p->next; deletep; --; } returnOK; } //从栈顶到栈底用Visit()函数遍历栈中每个数据元素 voidStackTraverse(Stacks,Status(*Visit)(ElemTypee)) { LinkTypep; p=; while(p)Visit(p->data); } 3.16假设如题3.1所属火车调度站的入口处有n节硬席或软席车厢(分别以H 和S表示)等待调度,试编写算法,输出对这n节车厢进行调度的操作(即入栈 或出栈操作)序列,以使所有的软席车厢都被调整到硬席车厢之前。 解: intmain() { Stacks; charBuffer[80]; inti=0,j=0; InitStack(s); cout<<"请输入硬席(H)和软席车厢(S)序列:"; cin>>Buffer; cout< while(Buffer[i]){ if(Buffer[i]=='S'){ Buffer[j]=Buffer[i]; j++; } elsePush(s,Buffer[i]); i++; } while(Buffer[j]){ Pop(s,Buffer[j]); j++; } cout< return0; } 3.17试写一个算法,识别一次读入的一个以@为结束符的字符序列是否为形如 ‘序列1&序列2’模式的字符序列。其中序列1和序列2中都不含字符‘&’, 且序列2是序列1的逆序列。例如,‘a+b&b+a’是属该模式的字符序列,而‘1+3&3-1’ 则不是。 解: BOOLSymmetry(chara[]) { inti=0; Stacks; InitStack(s); ElemTypex; while(a[i]!='&'&&a[i]){ Push(s,a[i]); i++; } if(a[i])returnFALSE; i++; while(a[i]){ Pop(s,x); if(x!=a[i]){ DestroyStack(s); returnFALSE; } i++; } returnTRUE; } 3.18试写一个判别表达式中开、闭括号是否配对出现的算法。 解: BOOLBracketCorrespondency(chara[]) { inti=0; Stacks; InitStack(s); ElemTypex; while(a[i]){ switch(a[i]){ case'(': Push(s,a[i]); break; case'[': Push(s,a[i]); break; case')': GetTop(s,x); if(x=='(')Pop(s,x); elsereturnFALSE; break; case']': GetTop(s,x); if(x=='[')Pop(s,x); elsereturnFALSE; break; default: break; } i++; } if(!=0)returnFALSE; returnTRUE; } 3.20假设以二维数组g(1…m,1…n)表示一个图像区域,g[i,j]表示该区域中 点(i,j)所具颜色,其值为从0到k的整数。编写算法置换点(i 0 ,j 0 )所在区域的 颜色。约定和(i 0 ,j 0 )同色的上、下、左、右的邻接点为同色区域的点。 解: #include #include typedefstruct{ intx; inty; }PosType; typedefstruct{ intColor; intVisited; PosTypeseat; }ElemType; #include"d:VC99Stack.h" #defineM8 #defineN8 ElemTypeg[M][N]; voidCreateGDS(ElemTypeg[M][N]); voidShowGraphArray(ElemTypeg[M][N]); voidRegionFilling(ElemTypeg[M][N],PosTypeCurPos,intNewColor); intmain() { CreateGDS(g); ShowGraphArray(g); PosTypeStartPos; StartPos.x=5; StartPos.y=5; intFillColor=6; RegionFilling(g,StartPos,FillColor); cout< ShowGraphArray(g); return0; } voidRegionFilling(ElemTypeg[M][N],PosTypeCurPos,intFillColor) { Stacks; InitStack(s); ElemTypee; intOldColor=g[CurPos.x][CurPos.y].Color; Push(s,g[CurPos.x][CurPos.y]); while(!StackEmpty(s)){ Pop(s,e); CurPos=; g[CurPos.x][CurPos.y].Color=FillColor; g[CurPos.x][CurPos.y].Visited=1; if(CurPos.x !g[CurPos.x+1][CurPos.y].Visited&& g[CurPos.x+1][CurPos.y].Color==OldColor ) Push(s,g[CurPos.x+1][CurPos.y]); if(CurPos.x>0&& !g[CurPos.x-1][CurPos.y].Visited&& g[CurPos.x-1][CurPos.y].Color==OldColor ) Push(s,g[CurPos.x-1][CurPos.y]); if(CurPos.y !g[CurPos.x][CurPos.y+1].Visited&& g[CurPos.x][CurPos.y+1].Color==OldColor ) Push(s,g[CurPos.x][CurPos.y+1]); if(CurPos.y>0&& !g[CurPos.x][CurPos.y-1].Visited&& g[CurPos.x][CurPos.y-1].Color==OldColor ) Push(s,g[CurPos.x][CurPos.y-1]); } } voidCreateGDS(ElemTypeg[M][N]) { inti,j; for(i=0;i for(j=0;j g[i][j].seat.x=i; g[i][j].seat.y=j; g[i][j].Visited=0; g[i][j].Color=0; } for(i=2;i<5;i++) for(j=2;j<4;j++) g[i][j].Color=3; for(i=5;i for(j=3;j<6;j++) g[i][j].Color=3; } voidShowGraphArray(ElemTypeg[M][N]) { inti,j; for(i=0;i for(j=0;j cout< cout< } } 3.21假设表达式有单字母变量和双目四则运算符构成。试写一个算法,将一个 通常书写形式且书写正确的表达式转换为逆波兰表达式。 解: //输入的表达式串必须为#...#格式 voidInversePolandExpression(charBuffer[]) { Stacks; InitStack(s); inti=0,j=0; ElemTypee; Push(s,Buffer[i]); i++; while(Buffer[i]!='#'){ if(!IsOperator(Buffer[i])){//是操作数 Buffer[j]=Buffer[i]; i++; j++; } else{//是操作符 GetTop(s,e); if(Prior(e,Buffer[i])){//当栈顶优先权高于当前序列时,退栈 Pop(s,e); Buffer[j]=e; j++; } else{ Push(s,Buffer[i]); i++; } } } while(!StackEmpty(s)){ Pop(s,e); Buffer[j]=e; j++; } } StatusIsOpertor(charc) { char*p="#+-*/"; while(*p){ if(*p==c) returnTRUE; p++; } returnFALSE; } StatusPrior(charc1,charc2) { charch[]="#+-*/"; inti=0,j=0; while(ch[i]&&ch[i]!=c1)i++; if(i==2)i--;//加和减可认为是同级别的运算符 if(i==4)i--;//乘和除可认为是同级别的运算符 while(ch[j]&&ch[j]!=c2)j++; if(j==2)j--; if(j==4)j--; if(i>=j)returnTRUE; elsereturnFALSE; } 3.22如题3.21的假设条件,试写一个算法,对以逆波兰式表示的表达式求值。 解: charCalVal_InverPoland(charBuffer[]) { StackOpnd; InitStack(Opnd); inti=0; charc; ElemTypee1,e2; while(Buffer[i]!='#'){ if(!IsOperator(Buffer[i])){ Push(Opnd,Buffer[i]); } else{ Pop(Opnd,e2); Pop(Opnd,e1); c=Cal(e1,Buffer[i],e2); Push(Opnd,c); } i++; } returnc; } charCal(charc1,charop,charc2) { intx,x1,x2; charch[10]; ch[0]=c1; ch[1]='0'; x1=atoi(ch); ch[0]=c2; ch[1]='0'; x2=atoi(ch); switch(op){ case'+': x=x1+x2; break; case'-': x=x1-x2; break; case'*': x=x1*x2; break; case'/': x=x1/x2; break; default: break; } itoa(x,ch,10); returnch[0]; } 3.23如题3.21的假设条件,试写一个算法,判断给定的非空后缀表达式是否为 正确的逆波兰表达式,如果是,则将它转化为波兰式。 解: #include #include #include #include"d:VC99DSConstant.h" typedefcharARRAY[30]; typedefARRAYElemType; typedefstructNodeType{ ElemTypedata; NodeType*next; }NodeType,*LinkType; typedefstruct{ LinkTypetop; intsize; }Stack; voidInitStack(Stack&s); StatusPush(Stack&s,ElemTypee); StatusPop(Stack&s,ElemTypee); StatusIsOperator(charc); StatusStackEmpty(Stacks); StatusInvToFroPoland(chara[]); intmain() { chara[30]; cout<<"请输入逆波兰算术表达式字符序列:"; cin>>a; if(InvToFroPoland(a))cout< elsecout<<"输入逆波兰算术表达式字符序列错误!"; return0; } StatusInvToFroPoland(chara[]) { Stacks; InitStack(s); inti=0; ElemTypech; ElemTypec1; ElemTypec2; while(a[i]!='#'){ if(!IsOperator(a[i])){ if(a[i]>='0'&&a[i]<='9'){ ch[0]=a[i];ch[1]='0'; Push(s,ch); } elsereturnFALSE; } else{ ch[0]=a[i]; ch[1]='0'; if(!StackEmpty(s)){ Pop(s,c2); if(!StackEmpty(s)){ Pop(s,c1); strcat(ch,c1); strcat(ch,c2); Push(s,ch); } elsereturnFALSE; } elsereturnFALSE; } i++; } if(!StackEmpty(s)){ Pop(s,c1); strcpy(a,c1); } elsereturnFALSE; if(!StackEmpty(s))returnFALSE; returnOK; } voidInitStack(Stack&s) { =NULL; =0; } StatusPush(Stack&s,ElemTypee) { LinkTypep; p=newNodeType; if(!p)exit(OVERFLOW); p->next=; =p; strcpy(p->data,e); ++; returnOK; } StatusPop(Stack&s,ElemTypee) { LinkTypep; if(){ strcpy(e,->data); p=; =p->next; deletep; --; } returnOK; } StatusStackEmpty(Stacks) { if(==0)returnTRUE; elsereturnFALSE; } StatusIsOperator(charc) { char*p="#+-*/"; while(*p){ if(*p==c) returnTRUE; p++; } returnFALSE; } 3.24试编写如下定义的递归函数的递归算法,并根据算法画出求g(5,2)时栈的 变化过程。 解: intg(intm,intn); intmain() { intm,n; cout<<"请输入m和n的值:"; cin>>m>>n; if(n>=0)cout< elsecout<<"NoSolution!"; return0; } intg(intm,intn) { if(m>0) return(g(m-1,2*n)+n); elsereturn0; } 假设主函数的返回地址为0,递归函数3条语句的地址分别为1、2、3。 3064 3132 3216 338 344 052 3.25试写出求递归函数F(n)的递归算法,并消除递归: 解: #include #defineN20 intmain() { inti; inta[N]; intn; cout<<"请输入n:"; cin>>n; for(i=0;i if(i<1)a[i]=1; elsea[i]=i*a[i/2]; } cout< return0; } 3.26求解平方根A的迭代函数定义如下: 其中,p是A的近似平方根,e是结果允许误差。试写出相应的递归算法,并消 除递归。 解: #include doubleSqrt(doubleA,doublep,doublee); intmain() { doubleA,p,e; cout<<"请输入Ape:"; cin>>A>>p>>e; cout< return0; } doubleSqrt(doubleA,doublep,doublee) { if((p*p-A)>-e&&(p*p-A) returnp; else returnSqrt(A,(p+A/p)/2,e); } 3.27已知Ackerman函数的定义如下: (1)写出递归算法; (2)写出非递归算法; (3)根据非递归算法,画出求akm(2,1)时栈的变化过程。 解: unsignedintakm(unsignedintm,unsignedintn) { unsignedintg; if(m==0) returnn+1; else if(n==0)returnakm(m-1,1); else{ g=akm(m,n-1); returnakm(m-1,g); } } 非递归算法: intakm1(intm,intn) { Stacks; InitStack(s); ElemTypee,e1,d; =m;=n; Push(s,e); do{ while(){ while(){ --; Push(s,e); } --;=1; } if(StackLength(s)>1){ =; Pop(s,e); --; =+1; } }while(StackLength(s)!=1||!=0); +1; } 0,akm(2,1)021g=akm(2,0) 1,akm(2,0)620akm=akm(m-1,1)=akm(1,1) 2,akm(1,1)411g=akm(m,n-1)=akm(1,0) 3,akm(1,0)610akm=akm(m-1,1)=akm(0,1) 4,akm(0,1)401akm=n+1=2退栈 0,akm(2,1)021g=akm(2,0) 1,akm(2,0)620akm=akm(m-1,1)=akm(1,1) 2,akm(1,1)411g=akm(m,n-1)=akm(1,0) 3,akm(1,0)610akm=akm(m-1,1)=akm(0,1)=2退栈 0,akm(2,1)021g=akm(2,0) 1,akm(2,0)620akm=akm(m-1,1)=akm(1,1) 2,akm(1,1)411g=akm(m,n-1)=akm(1,0)=2; akm=akm(m-1,g)=akm(0,2); 3,akm(0,2)702akm=n+1=3退栈 0,akm(2,1)021g=akm(2,0) 1,akm(2,0)620akm=akm(m-1,1)=akm(1,1) 2,akm(1,1)411g=akm(m,n-1)=akm(1,0)=2; akm=akm(m-1,g)=akm(0,2)=3;退栈 0,akm(2,1)021g=akm(2,0) 1,akm(2,0)620akm=akm(m-1,1)=akm(1,1)=3退栈 0,akm(2,1)021g=akm(2,0)=3;akm=akm(1,3) 1,akm(1,3)613g=akm(1,2);akm(m-1,g) 2,akm(1,2)612g=akm(1,1);akm(m-1,g) 3,akm(1,1)611g=akm(1,0);akm(m-1,g) 4,akm(1,0)610akm=akm(0,1) 5,akm(0,1)401akm(0,1)=2退栈 0,akm(2,1)021g=akm(2,0)=3;akm=akm(1,3) 1,akm(1,3)613g=akm(1,2);akm(m-1,g) 2,akm(1,2)612g=akm(1,1);akm(m-1,g) 3,akm(1,1)611g=akm(1,0);akm(m-1,g) 4,akm(1,0)610akm=akm(0,1)=2退栈 0,akm(2,1)021g=akm(2,0)=3;akm=akm(1,3) 1,akm(1,3)613g=akm(1,2);akm(m-1,g) 2,akm(1,2)612g=akm(1,1);akm(m-1,g) 3,akm(1,1)611g=akm(1,0)=2;akm(m-1,g)=akm(0,2) 4,akm(0,2)702akm=n+1=3退栈 0,akm(2,1)021g=akm(2,0)=3;akm=akm(1,3) 1,akm(1,3)613g=akm(1,2);akm(m-1,g) 2,akm(1,2)612g=akm(1,1);akm(m-1,g) 3,akm(1,1)611g=akm(1,0)=2;akm(m-1,g)=akm(0,2)=3退栈 0,akm(2,1)021g=akm(2,0)=3;akm=akm(1,3) 1,akm(1,3)613g=akm(1,2);akm(m-1,g) 2,akm(1,2)612g=akm(1,1)=3;akm(m-1,g)=akm(0,3) 3,akm(0,3)703akm=n+1=4退栈 0,akm(2,1)021g=akm(2,0)=3;akm=akm(1,3) 1,akm(1,3)613g=akm(1,2);akm(m-1,g) 2,akm(1,2)612g=akm(1,1)=3;akm(m-1,g)=akm(0,3)=4退栈 0,akm(2,1)021g=akm(2,0)=3;akm=akm(1,3) 1,akm(1,3)613g=akm(1,2)=4;akm(m-1,g)=akm(0,4) 2,akm(0,4)704akm=n+1=5退栈 0,akm(2,1)021g=akm(2,0)=3;akm=akm(1,3) 1,akm(1,3)613g=akm(1,2)=4;akm(m-1,g)=akm(0,4)=5退栈 0,akm(2,1)021g=akm(2,0)=3;akm=akm(1,3)=5退栈 akm(2,1)=5; 3.28假设以带头结点的循环链表表示队列,并且只设一个指针指向队尾元素结 点(注意不设头指针),试编写相应的队列初始化、入队列何处队列的算法。 解: typedefintElemType; typedefstructNodeType{ ElemTypedata; NodeType*next; }QNode,*QPtr; typedefstruct{ QPtrrear; intsize; }Queue; StatusInitQueue(Queue&q) { =NULL; =0; returnOK; } StatusEnQueue(Queue&q,ElemTypee) { QPtrp; p=newQNode; if(!p)returnFALSE; p->data=e; if(!){ =p; p->next=; } else{ p->next=->next; ->next=p; =p; } ++; returnOK; } StatusDeQueue(Queue&q,ElemType&e) { QPtrp; if(==0)returnFALSE; if(==1){ p=; e=p->data; =NULL; deletep; } else{ p=->next; e=p->data; ->next=p->next; deletep; } --; returnOK; } 3.29如果希望循环队列中的元素都能得到利用,则需设置一个标志域tag,并 以tag的值为0和1来区分,尾指针和头指针值相同时的队列状态是“空”还是 “满”。试编写与此结构相应的入队列和出队列的算法,并从时间和空间角度讨 论设标志和不设标志这两种方法的使用范围(如当循环队列容量较小而队列中每 个元素占的空间较多时,哪一种方法较好)。 解: #defineMaxQSize4 typedefintElemType; typedefstruct{ ElemType*base; intfront; intrear; Statustag; }Queue; StatusInitQueue(Queue&q) { =newElemType[MaxQSize]; if(!)returnFALSE; =0; =0; =0; returnOK; } StatusEnQueue(Queue&q,ElemTypee) { if(==&&)returnFALSE; else{ []=e; =(+1)%MaxQSize; if(==)=1; } returnOK; } StatusDeQueue(Queue&q,ElemType&e) { if(==&&!)returnFALSE; else{ e=[]; =(+1)%MaxQSize; =0; } returnOK; } 设标志节省存储空间,但运行时间较长。不设标志则正好相反。 3.30假设将循环队列定义为:以域变量rear和length分别指示循环队列中队尾 元素的位置和内含元素的个数。试给出此循环队列的队满条件,并写出相应的入 队列和出队列的算法(在出队列的算法中要返回队头元素)。 解: #defineMaxQSize4 typedefintElemType; typedefstruct{ ElemType*base; intrear; intlength; }Queue; StatusInitQueue(Queue&q) { =newElemType[MaxQSize]; if(!)returnFALSE; =0; =0; returnOK; } StatusEnQueue(Queue&q,ElemTypee) { if((+1)%MaxQSize==(+)%MaxQSize) returnFALSE; else{ []=e; =(+1)%MaxQSize; ++; } returnOK; } StatusDeQueue(Queue&q,ElemType&e) { if((+)%MaxQSize==) returnFALSE; else{ e=[(+)%MaxQSize]; --; } returnOK; } 3.31假设称正读和反读都相同的字符序列为“回文”,例如,‘abba’和‘abcba’ 是回文,‘abcde’和‘ababab’则不是回文。试写一个算法判别读入的一个以‘@’为结 束符的字符序列是否是“回文”。 解: StatusSymmetryString(char*p) { Queueq; if(!InitQueue(q))return0; Stacks; InitStack(s); ElemTypee1,e2; while(*p){ Push(s,*p); EnQueue(q,*p); p++; } while(!StackEmpty(s)){ Pop(s,e1); DeQueue(q,e2); if(e1!=e2)returnFALSE; } returnOK; } 3.32试利用循环队列编写求k阶菲波那契序列中前n+1项的算法,要求满足: max n f而max 1 n f,其中max为某个约定的常数。(注意:本题所用循环队 列的容量仅为k,则在算法执行结束时,留在循环队列中的元素应是所求k阶菲 波那契序列中的最后k项) 解: intFibonacci(intk,intn) { if(k<1)exit(OVERFLOW); Queueq; InitQueue(q,k); ElemTypex,e; inti=0; while(i<=n){ if(i if(!EnQueue(q,0))exit(OVERFLOW); } if(i==k-1){ if(!EnQueue(q,1))exit(OVERFLOW); } if(i>=k){ //队列求和 x=sum(q); DeQueue(q,e); EnQueue(q,x); } i++; } [(+e-1)%e]; } 3.33在顺序存储结构上实现输出受限的双端循环队列的入列和出列(只允许队 头出列)算法。设每个元素表示一个待处理的作业,元素值表示作业的预计时间。 入队列采取简化的短作业优先原则,若一个新提交的作业的预计执行时间小于队 头和队尾作业的平均时间,则插入在队头,否则插入在队尾。 解: //Filename:Queue.h typedefstruct{ ElemType*base; intfront; intrear; Statustag; intMaxSize; }DQueue; StatusInitDQueue(DQueue&q,intsize) { e=size; =newElemType[e]; if(!)returnFALSE; =0; =0; =0; returnOK; } StatusEnDQueue(DQueue&q,ElemTypee) { if(==&&)returnFALSE; if(==&&!){//空队列 []=e; =(+1)%e; if(==)=1; } else{//非空非满 if(e<([]+[(+e-1)%e])/2){ //从队头入队 =(+e-1)%e; []=e; if(==)=1; } else{//从队尾入队 []=e; =(+1)%e; if(==)=1; } } returnOK; } StatusDeDQueue(DQueue&q,ElemType&e) { if(==&&!)returnFALSE; else{//非空队列 e=[]; =(+1)%e; =0; } returnOK; } //Filename:主程序文件 #include #include typedefintElemType; #include"D:VC99Queue.h" intmain() { intt1,t2,t3,t4; ElemTypee; cout<<"请输入作业a1、a2、a3、a4的执行时间:"; cin>>t1>>t2>>t3>>t4; DQueuedq; InitDQueue(dq,5); EnDQueue(dq,t1); EnDQueue(dq,t2); EnDQueue(dq,t3); EnDQueue(dq,t4); while(!=||){ DeDQueue(dq,e); cout< } return0; } 3.34‘E’和‘D’表示对双端队列的头端进行入队列和出队列的操作;以字符A表示 对双端队列的尾端进行入队列的操作。 解: intmain() { ElemTypee; DQueuedq; InitDQueue(dq,20); charch[20]; cout<<"请输入待调度的车厢字符序列(仅限PHS):"; cin>>ch; inti=0; while(ch[i]){ if(ch[i]=='P')cout< if(ch[i]=='S')EnDQueue(dq,ch[i],0);//从队头入队 if(ch[i]=='H')EnDQueue(dq,ch[i],1);//从队尾入队 i++; } while(!=||){ DeDQueue(dq,e); cout< } cout< return0; } 第4章串 4.1解:空格串是指一个或多个空格字符(ASCII码为20H)组成的串,而空串中 没有任何字符。 4.2解:串赋值(StrAssign)、串比较(StrCompare)、求串长(StrLength)、串连 接(Concat)、求子串(SubString)这五种基本操作构成串类型的最小操作子集。 4.6解: s1=SubString(s,3,1) s2=SubString(s,6,1) Replace(s,s1,s2) Concat(s3,s,s1) Concat(t,SubString(s3,1,5),SubString(s3,7,2)) 算法设计题: //Filename:String.h #include #include #defineMaxSize128 classString{ char*ch; intcurlen; public: String(constString&ob); String(constchar*init); String(); ~String(); voidStrAssign(Stringt); intStrCompare(Stringt); intStrLength(); voidConcat(Stringt); StringSubString(intstart,intlen); voidshow(); }; String::String(constString&ob) { ch=newchar[MaxSize+1]; if(!ch)exit(1); curlen=; strcpy(ch,); } String::String(constchar*init) { ch=newchar[MaxSize+1]; if(!ch)exit(1); curlen=strlen(init); strcpy(ch,init); } String::String() { ch=newchar[MaxSize+1]; if(!ch)exit(1); curlen=0; ch[0]='0'; } String::~String() { deletech; curlen=0; } voidString::StrAssign(Stringt) { strcpy(ch,); curlen=; } intString::StrCompare(Stringt) { returnstrcmp(ch,); } intString::StrLength() { returncurlen; } voidString::Concat(Stringt) { strcat(ch,); curlen=curlen+; } StringString::SubString(intstart,intlen) { Stringtemp; inti,j; if(start>=0&&start+len0){ =len; for(i=0,j=start;i [i]=ch[j]; [len]='0'; } returntemp; } voidString::show() { cout< } 4.10解: voidStrReverse(String&s) { Stringt; inti,j; j=gth(); for(i=j-1;i>=0;i--) (ing(i,1)); ign(t); } 4.11解: 第5章数组与广义表 5.1解: (1)6×8×6=288Byte (2)LOC(5,7)=1000+(5×8+7)×6=1282 (3)LOC(1,4)=1000+(1×8+4)×6=1072 (4)LOC(4,7)=1000+(7×6+4)×6=1276 5.2解: (1)LOC(0,0,0,0)=100 (2)LOC(1,1,1,1)=100+(1×3×5×8+1×5×8+1×8+1)×4=776 (3)LOC(3,1,2,5)=100+(3×3×5×8+1×5×8+2×8+5)×4=1784 (4)LOC(8,2,4,7)=100+(8×3×5×8+2×5×8+4×8+7)×4=4416 5.3解: (0,0,0,0)(1,0,0,0)(0,1,0,0)(1,1,0,0)(0,0,1,0)(1,0,1,0)(0,1,1,0) (1,1,1,0) (0,0,2,0)(1,0,2,0)(0,1,2,0)(1,1,2,0)(0,0,0,1)(1,0,0,1)(0,1,0,1) (1,1,0,1) (0,0,1,1)(1,0,1,1)(0,1,1,1)(1,1,1,1)(0,0,2,1)(1,0,2,1)(0,1,2,1) (1,1,2,1) (0,0,0,2)(1,0,0,2)(0,1,0,2)(1,1,0,2)(0,0,1,2)(1,0,1,2)(0,1,1,2) (1,1,1,2) (0,0,2,2)(1,0,2,2)(0,1,2,2)(1,1,2,2) 5.4解: b 2 )1a(a k 其中,a=Max(i,j),b=Min(i,j) 5.5解: 1 2 )1i(i )jn(nik (1i,1j,ji) 5.6解:u=i-j+1v=j-1 5.7解:(1)k=2(i-1)+j-1(1ji) (2)i=(k+1)DIV3+1( 1n3k0 ) j=k+1-2(kDIV3) 5.8解:i为奇数时,k=i+j-2 j为偶数时,k=i+j-1 k=2(iDIV2)+j-1 5.9解:设稀疏矩阵为n行n列,其中的非零元为m个,m远小于2n 。从时间上 来说,采用二维数组存储稀疏矩阵需要2n -1次加法运算,而用三元组只需m-1 次加法运算。从空间上来说,用二维数组需要2n 个基本存储单元,而三元组需 要m个基本存储单元外加2m个整型存储单元。由于2n 远远大于m,故实际存储 空间也较大。 5.10解: (1)GetHead【(p,h,w)】=p (2)GetTail【(b,k,p,h)】=(k,p,h) (3)GetHead【((a,b),(c,d))】=(a,b) (4)GetTail【((a,b),(c,d))】=((c,d)) (5)GetHead【GetTail【((a,b),(c,d))】】=GetHead【((c,d))】=(c,d) (6)GetTail【GetHead【((a,b),(c,d))】】=GetTail【(a,b)】=(b) (7)GetHead【GetTail【GetHead【((a,b),(c,d))】】】=GetHead【(b)】 =b (8)GetTail【GetHead【GetTail【((a,b),(c,d))】】】=GetTail【(c,d)】 =(d) 5.11解: (1)GetHead【GetTail【GetTail【L1】】】 (2)GetHead【GetHead【GetTail【L2】】】 (3)GetHead【GetHead【GetTail【GetTail【GetHead【L3】】】】】 (4)GetHead【GetHead【GetHead【GetTail【GetTail【L4】】】】】 (5)GetHead【GetHead【GetTail【GetTail【L5】】】】 (6)GetHead【GetTail【GetHead【L6】】】 (7)GetHead【GetHead【GetTail【GetHead【GetTail【L7】】】】】 5.12解: 5.13解: (1)List=((x,(y)),(((())),(()),(z))) (2)List=(((a,b,()),()),(a,(b)),()) 5.14解:d)1n(a)1n(sa)1n(s)n(s 1n (n>=1) ElemTypes(inti) { if(i>1) returns(i-1)+a1+(i-1)*d; else returna1; } 5.16解: 0b 0b )b,a(add a )b,a(add 5.17解: intMax(SqList&L,intk) { if(k<-1) if([k] returnMax(L,k+1); else [k]; else [k]; } intMin(SqList&L,intk) { if(k<-1) if([k]>Min(L,k+1)) returnMin(L,k+1); else [k]; else [k]; } intSum(SqList&L,intk) { if(k==0) [0]; else [k]+Sum(a,k-1); } intProduct(SqList&L,intk) { if(k==0) [0]; else [k]*Sum(a,k-1); } doubleAvg(SqList&L,intk) { if(k==0) [0]; else return(Avg(a,k-1)*k+[k])/(k+1); } 5.18解:算法的基本思想是将数组分成k组,将第一组与第二组进行两两交换, 再将第一组与第三组进行两两交换,...,总共需进行n-k次交换。注意最后一 组可能出现不足k个元素的情况,此时最后一组为剩余元素加第一组的前几个元 素共k个构成最后一组。 voidRRMove(ElemTypeA[],intk,intn) { ElemTypee; inti=0,j,p; while(i p=i/k+1; for(j=0;j e=A[j]; A[j]=A[(p*k+j)%n]; A[(p*k+j)%n]=e; i++; } } } 5.19解: #include #defineRS4 #defineCS4 typedefintElemType; typedefstruct{ ElemTypee; inti,j; intFlags; }NodeType; voidInitialize(NodeTypea[RS][CS],ElemTypeA[RS][CS]); voidSaddlePoint(NodeTypea[RS][CS]); ElemTypeRowMin(NodeTypea[RS][CS],intk); ElemTypeColMax(NodeTypea[RS][CS],intk); voidShow(NodeTypea[RS][CS]); intmain() { ElemTypeA[RS][CS]={ {2,1,3,4}, {1,3,1,2}, {2,7,1,3}, {3,2,4,1} }; NodeTypea[RS][CS]; Initialize(a,A); SaddlePoint(a); Show(a); return0; } voidInitialize(NodeTypea[RS][CS],ElemTypeA[RS][CS]) { inti,j; for(i=0;i for(j=0;j a[i][j].e=A[i][j]; a[i][j].i=i; a[i][j].j=j; a[i][j].Flags=0; } } } voidSaddlePoint(NodeTypea[RS][CS]) { inti,j; ElemTypex,y; for(i=0;i x=RowMin(a,i); for(j=0;j y=ColMax(a,j); if(a[i][j].e==x&&a[i][j].e==y) a[i][j].Flags=1; } } } ElemTypeRowMin(NodeTypea[RS][CS],intk) { ElemTypex; x=a[k][0].e; inti; for(i=1;i if(x>a[k][i].e){ x=a[k][i].e; } returnx; } ElemTypeColMax(NodeTypea[RS][CS],intk) { ElemTypex; x=a[0][k].e; inti; for(i=1;i if(x x=a[i][k].e; } returnx; } voidShow(NodeTypea[RS][CS]) { for(inti=0;i for(intj=0;j if(a[i][j].Flags) cout< } 5.21解: typedefintElemType; classTriple{ public: introw; intcol; ElemTypee; Triple(){} virtual~Triple(){} BOOLoperator<(Tripleb); BOOLoperator==(Tripleb); }; BOOLTriple::operator<(Tripleb) { if(row<)returnTRUE; if(row==&&col<)returnTRUE; returnFALSE; } BOOLTriple::operator==(Tripleb) { if(row==&&col==) returnTRUE; else returnFALSE; } classCSparseMat { public: CSparseMat(){} virtual~CSparseMat(){} CSparseMat(intr,intc,intn); CSparseMatoperator+(CSparseMatB); voidShowSparse(CDC*pDC); Triple*m_pt;//指向非零元的指针 intm_nCol;//矩阵列数 intm_nRow;//矩阵行数 intm_nTrs;//非零元个数 }; CSparseMat::CSparseMat(intr,intc,intn) { m_nRow=r; m_nCol=c; m_nTrs=n; m_pt=newTriple[m_nTrs]; //输入矩阵的所有三元组 inti; for(i=0;i CInputDlgdlg1; if(l()==IDOK){ m_pt[i].row=dlg1.m_nRow; m_pt[i].col=dlg1.m_nCol; m_pt[i].e=dlg1.m_nElem; } } } voidCSparseMat::ShowSparse(CDC*pDC) { charstr[10]; intk=0; for(inti=0;i for(intj=0;j if(m_pt[k].row==i&&m_pt[k].col==j){ itoa(m_pt[k].e,str,10); k++; } elseitoa(0,str,10); pDC->TextOut(0+j*20,0+i*20,str,strlen(str)); } } } //矩阵相加的运算符重载函数 CSparseMatCSparseMat::operator+(CSparseMatB) { CSparseMattemp(m_nRow,m_nCol,0); if(m_nRow!=B.m_nRow||m_nCol!=B.m_nCol) returntemp; temp.m_pt=newTriple[m_nTrs+B.m_nTrs]; if(!temp.m_pt)returntemp; temp.m_nTrs=m_nTrs+B.m_nTrs; inti=0; intj=0; intk=0; while(i if(m_pt[i] temp.m_pt[k].row=m_pt[i].row; temp.m_pt[k].col=m_pt[i].col; temp.m_pt[k].e=m_pt[i].e; i++; } else{ if(m_pt[i]==B.m_pt[j]){ temp.m_pt[k].row=m_pt[i].row; temp.m_pt[k].col=m_pt[i].col; temp.m_pt[k].e=m_pt[i].e+B.m_pt[j].e; i++;j++; } else{ temp.m_pt[k].row=B.m_pt[j].row; temp.m_pt[k].col=B.m_pt[j].col; temp.m_pt[k].e=B.m_pt[j].e; j++; } } k++; } while(i temp.m_pt[k].row=m_pt[i].row; temp.m_pt[k].col=m_pt[i].col; temp.m_pt[k].e=m_pt[i].e; i++; k++; } while(j temp.m_pt[k].row=B.m_pt[j].row; temp.m_pt[k].col=B.m_pt[j].col; temp.m_pt[k].e=B.m_pt[j].e; j++; k++; } temp.m_nTrs=k; returntemp; } 5.23解: #include #include #defineMax128 typedefintElemType; typedefstruct{ intcol; ElemTypee; }Twin; classCSparseMat { public: CSparseMat(){} CSparseMat(intr,intc,intn); virtual~CSparseMat(){} voidShowSparse(inti,intj); Twin*m_pt;//指向非零元的指针 intrpos[Max]; intm_nCol;//矩阵列数 intm_nRow;//矩阵行数 intm_nTws;//非零元个数 }; CSparseMat::CSparseMat(intr,intc,intn) { m_nRow=r; m_nCol=c; m_nTws=n; m_pt=newTwin[m_nTws]; if(!m_pt)return; //输入矩阵的所有二元组 inti; for(i=0;i cout<<"请输入非零元二元组的列标和值:"; cin>>m_pt[i].col>>m_pt[i].e; } for(i=0;i cout<<"请输入每行第一个非零元在二元组中的序号(没有输入-1):"; cin>>rpos[i];//该行没有非零元输入-1 } } voidCSparseMat::ShowSparse(inti,intj) { if(i>m_nRow||j>m_nCol)return; ElemTypex=0; ints,d; if(i==m_nRow){ s=rpos[i]; d=m_nTws; } else{ s=rpos[i]; intm=1; d=rpos[i+m]; while(d<0){ if(i+m m++; d=rpos[i+m]; } else d=m_nTws; } } if(s>=0){ intk=s; while(k if(m_pt[k].col==j) x=m_pt[k].e; k++; } } cout< } intmain() { CSparseMatA(3,3,5); arse(2,1); return0; } 5.26解: typedefintElemType; typedefstructOLNode{ introw; intcol; ElemTypee; structOLNode*right,*down; }OLNode,*OLink; classCCrossListMat { public: OLink*RHead,*CHead;//行与列指针向量的头指针 intm_nCol;//矩阵列数 intm_nRow;//矩阵行数 intm_nNum;//非零元个数 public: CCrossListMat(){} CCrossListMat(intr,intc,intn); virtual~CCrossListMat(){} voidShowMat(inti,intj); }; CCrossListMat::CCrossListMat(intr,intc,intn) { m_nRow=r; m_nCol=c; m_nNum=n; inti; RHead=newOLink[m_nRow]; if(!RHead)exit(-2); CHead=newOLink[m_nCol]; if(!CHead)exit(-2); for(i=0;i RHead[i]=NULL; for(i=0;i CHead[i]=NULL; OLinkp,q,qf; for(i=0;i p=newOLNode; if(!p)exit(-2); cout<<"请输入非零元的行标、列标和值:"; cin>>p->row>>p->col>>p->e; q=RHead[p->row]; if(!q){ RHead[p->row]=p; p->right=NULL; } else{ qf=q; while(q&&q->colcol){ qf=q; q=q->right; } p->right=qf->right; qf->right=p; } q=CHead[p->col]; if(!q){ CHead[p->col]=p; p->down=NULL; } else{ qf=q; while(q&&q->rowrow){ qf=q; q=q->down; } p->down=qf->down; qf->down=p; } } } voidCCrossListMat::ShowMat(inti,intj) { ElemTypex=0; OLinkp; p=RHead[i]; while(p&&p->col!=j) p=p->right; if(p) x=p->e; cout< } 5.27解: #include #include typedefintElemType; typedefstructOLNode{ introw; intcol; ElemTypee; structOLNode*right,*down; }OLNode,*OLink; classCCrossListMat { public: OLink*RHead,*CHead;//行与列指针向量的头指针 intm_nCol;//矩阵列数 intm_nRow;//矩阵行数 intm_nNum;//非零元个数 public: CCrossListMat(){} virtual~CCrossListMat(){} CCrossListMat(intr,intc,intn); voidAdd(CCrossListMatB); voidShowMat(); }; CCrossListMat::CCrossListMat(intr,intc,intn) { m_nRow=r; m_nCol=c; m_nNum=n; inti; RHead=newOLink[m_nRow]; if(!RHead)exit(-2); CHead=newOLink[m_nCol]; if(!CHead)exit(-2); for(i=0;i RHead[i]=NULL; for(i=0;i CHead[i]=NULL; OLinkp,q,qf; for(i=0;i p=newOLNode; if(!p)exit(-2); cout<<"请输入非零元的行标、列标和值:"; cin>>p->row>>p->col>>p->e; q=RHead[p->row]; if(!q){ RHead[p->row]=p; p->right=NULL; } else{ qf=q; while(q&&q->colcol){ qf=q; q=q->right; } p->right=qf->right; qf->right=p; } q=CHead[p->col]; if(!q){ CHead[p->col]=p; p->down=NULL; } else{ qf=q; while(q&&q->rowrow){ qf=q; q=q->down; } p->down=qf->down; qf->down=p; } } } voidCCrossListMat::Add(CCrossListMatB) { inti,k=0; OLinkpa,pb; OLinkpre,p;//按行插入 OLinkqpre,q;//按列插入 for(i=0;i pa=RHead[i]; pb=[i]; pre=NULL; while(pb){ while(pa&&pa->colcol){ pre=pa; pa=pa->right; } if(pa&&pa->col==pb->col){ pa->e=pa->e+pb->e; pb=pb->right; pre=pa; pa=pa->right; } else{ //在A中插入一个新结点 p=newOLNode; p->row=pb->row; p->col=pb->col; p->e=pb->e; pb=pb->right; if(!pre){ p->right=pa; RHead[i]=p; } else{ p->right=pre; pre->right=p; } //处理列指针 qpre=NULL; q=CHead[p->col]; while(q&&q->row qpre=q; q=q->down; } if(!qpre){ p->down=q; CHead[p->col]=p; } else{ p->down=pre; pre->down; } k++; } }//endwhile(pb) }//endfor m_nNum=m_nNum+k; } voidCCrossListMat::ShowMat() { inti,j; OLinkp; for(i=0;i p=RHead[i]; for(j=0;j if(p&&p->row==i&&p->col==j){ cout p=p->right; } else cout<<0<<""; } cout< } } intmain() { CCrossListMatA(3,3,4),B(3,3,2); (B); t(); return0; } 以下是关于广义表算法涉及的描述及方法 #include"DSConst.h"//常量定义头文件 #include"StrStat.h"//字符串定义头文件 //广义表数据结构声明 typedefcharAtomType; typedefenum{ATOM,LIST}ElemTag; typedefstructGLNode{ ElemTagtag; union{ AtomTypeatom; structGLNode*hp; }; structGLNode*tp; }*GList; //将非空串Str分割成两部分,HStr为第一个,TStr为之后的子串 intStrDistrict(CString&Str,CString&HStr,CString&TStr) { intn,i,k; CStrings1; CStrings2(","),s3("("),s4(")");//定义常量串 n=gth(); i=1; k=0; while(i<=n&&pare(s2)||k!=0){ s1=ing(i,1); if(!pare(s3))k++; elseif(!pare(s4))k--; i++; } if(i<=n){ HStr=ing(1,i-2); TStr=ing(i,n-i+1); } else{ HStr=Str; ar(); } returnOK; } //用串s建立广义表L intCreateGList(GList&L,CString&s) { CStringSub,HSub,TSub;//子串,表头串,表尾串 if(ty())L=NULL; else{//非空串,建立广义表 L=newGLNode;//开辟一个结点 if(!L)exit(OVERFLOW); if(gth()>1){//如果串长大于1,说明是表结点 L->tag=LIST; Sub=ing(2,gth()-2);//取括号内子串 if(!ty()){//建立子表 StrDistrict(Sub,HSub,TSub); if(!ty())//表头不空 CreateGList(L->hp,HSub); elseL->hp=NULL; if(!ty())//表尾不空 CreateGList(L->tp,TSub); elseL->tp=NULL; } else{//空表 L->hp=NULL; L->tp=NULL; } } else{//建立原子结点 L->tag=ATOM; L->atom=()[0]; L->tp=NULL; } } returnOK; } //显示广义表串 voidShowGList(GList&L) { if(L){ if(L->tag==LIST){ cout<<"("; if(L->hp) ShowGList(L->hp); if(L->tp){ cout<<","; ShowGList(L->tp); } cout<<")"; } elsecout } } 5.30解: //求广义表深度的递归算法 intGListDepth(GList&L) { intDepth=0; intHDepth,TDepth;//表头深度,表尾深度 if(!L)returnDepth;//广义表不存在 if(L->tag==ATOM)returnDepth;//原子结点深度为0 else{ Depth++;//表结点深度为1 HDepth=Depth+GListDepth(L->hp); TDepth=Depth+GListDepth(L->tp); returnHDepth>TDepth?HDepth:TDepth; } } 5.31解: //由广义表L复制广义表T intCopyGList(GList&T,GList&L) { if(!L)T=NULL; else{ T=newGLNode; if(!T)exit(OVERFLOW); T->tag=L->tag; if(L->tag==ATOM)T->atom=L->atom; else{ CopyGList(T->hp,L->hp); CopyGList(T->tp,L->tp); } } returnOK; } 5.32解: //判两广义表是否相等,相等返回OK,否则返回FALSE StatusGListCompare(GList&L1,GList&L2) { if(!L1&&!L2)returnOK;//L1和L2均为空表 if((!L1&&L2)||(L1&&!L2))returnFALSE; else{//L1和L2均非空表 if(L1->tag==L2->tag){//表属性相同 if(L1->tag==ATOM){//均为原子结点 if(L1->atom==L2->atom)returnOK; elsereturnFALSE; } else{//均为表结点 if(GListCompare(L1->hp,L2->hp)&& GListCompare(L1->tp,L2->tp)) returnOK;//表头、表尾均相同 elsereturnFALSE; } } elsereturnFALSE;//表属性不同 } } 5.33解: 6章树和二叉树 6.1已知一棵树边的集合为{,,,,,,, ,,,},请画出这棵树,并回答下列问题: (1)哪个是根结点? (2)哪些是叶子结点? (3)哪个是结点G的双亲? (4)哪些是结点G的祖先? (5)哪些是结点G的孩子? (6)哪些是结点E的子孙? (7)那些是结点E的子孙? (8)结点B和N的层次号分别是什么? (9)树的深度是多少? (10)以结点C为根的子树的深度是多少? 6.2一棵度为2的树与一棵二叉树有何区别? 解:二叉树是颗有序树,但度为2的树则未必有序。 6.3试分别画出具有3个结点的树和3个结点的二叉树的所有不同形态。 6.4一棵深度为H的满k叉树有如下性质:第H层上的结点都是叶子结点,其余 各层上每个结点都有k棵非空子树。如果按层次顺序从1开始对全部结点编号, 问: (1)各层的结点数目是多少? (2)编号为p的结点的父结点(若存在)的编号是多少? (3)编号为p的结点的第i个儿子结点(若存在)的编号是多少? (4)编号为p的结点有右兄弟的条件是什么?其右兄弟的编号是多少? 解:(1) 1 1 k kH (2)如果p是其双亲的最小的孩子(右孩子),则p减去根结点的一个结点, 应是k的整数倍,该整数即为所在的组数,每一组为一棵满k叉树,正好应为双 亲结点的编号。如果p是其双亲的最大的孩子(左孩子),则p+k-1为其最小的 弟弟,再减去一个根结点,除以k,即为其双亲结点的编号。 综合来说,对于p是左孩子的情况,i=(p+k-2)/k;对于p是右孩子的情况, i=(p-1)/k 如果左孩子的编号为p,则其右孩子编号必为p+k-1,所以,其双亲结点的编号 为 k kp i 2 向下取整,如1.5向下取整为1 (3)结点p的右孩子的编号为kp+1,左孩子的编号为kp+1-k+1=k(p-1)+2, 第i个孩子的编号为k(p-1)+2+i-1=kp-k+i+1。 (4)当(p-1)%k!=0时,结点p有右兄弟,其右兄弟的编号为p+1。 6.5已知一棵度为k的树中有 1 n个度为1的结点, 2 n个度为2的结点,…, k n个 度为k的结点,问该树中有多少个叶子结点? 解:根据树的定义,在一颗树中,除树根结点外,每个结点有且仅有一个前 驱结点,也就是说,每个结点与指向它的一个分支一一对应,所以除树根结点之 外的结点树等于所有结点的分支数,即度数,从而可得树中的结点数等于所有结 点的度数加1。总结点数为 而度为0的结点数就应为总结点数减去度不为0的结点数的总和,即 6.6已知在一棵含有n个结点的树中,只有度为k的分支结点和度为0的叶子结 点。试求该树含有的叶子节点数目。 解:利用上题结论易得结果。设度为k的结点个数为 k n,则总结点数为 knn k 1。叶子结点的数目应等于总结点数减去度不为0的结点的数目,即 6.7一棵含有n个结点的k叉树,可能达到的最大深度和最小深度各为多少? 解:能达到最大深度的树是单支树,其深度为n。满k叉树的深度最小,其 深度为 )1)1((logkn k (证明见徐孝凯着数据结构实用教程P166) 6.8证明:一棵满k叉树上的叶子结点数 0 n和非叶子结点数 1 n之间满足以下关 系: 解:一棵满k叉树的最后一层(深度为h)的结点数(叶子结点数)为1 0 hkn, 其总结点数为 1 1 k kh ,则非叶子结点数 0 0 011 1 1 1 n k kn n k k n h ,从而得 6.9试分别推导含有n个结点和含 0 n个叶子结点的完全三叉树的深度H。 解:(1)根据完全三叉树的定义 (2)设总的结点数为n,非叶子结点数为 1 n注意到每个非叶子结点的度均为 3,则 nn 1 31由nnn 10 6.10对于那些所有非叶子结点均含有左右子数的二叉树: (1)试问:有n个叶子结点的树中共有多少个结点? (2)试证明: 12 1 1 n i l i,其中n为叶子结点的个数, i l表示第i个叶子结 点所在的层次(设根节点所在层次为1)。 解:(1)总结点数为 1 21n,其中 1 n为非叶子结点数,则叶子结点数为 1 1 nn,所以总结点数为 12n 。 (2)用归纳法证明。 i=1,说明二叉树只有一个叶子结点,则整棵树只有一个根结点,1 1 l, 12)1( 1l,结论成立。 设有n个叶子结点时也成立,即12...2...22)1( )1( )1()1( 21 n p l l ll, 现假设增加一个叶子结点,这意味着在某叶子结点p上新生两个叶子结点,而结 点p则成为非叶子结点,可见,总结点数增2,叶子结点数增1。此时,所有叶 子结点是原结点除去p,然后加上两个深度为1 p l的新叶子结点,由此, 则当i=n+1时,也成立,由此即得到证明。 6.11在二叉树的顺序存储结构中,实际上隐含着双亲的信息,因此可和三叉链 表对应。假设每个指针域占4个字节,每个信息域占k个字节。试问:对于一棵 有n个结点的二叉树,且在顺序存储结构中最后一个节点的下标为m,在什么条 件下顺序存储结构比三叉链表更节省空间? 解:采用三叉链表结构,需要n(k+12)个字节的存储空间。采用顺序存储结 构,需要mk个字节的存储空间,则当mk nm n k 12 时,采用顺序 存储比采用三叉链表更节省空间。 6.12对题6.3所得各种形态的二叉树,分别写出前序、中序和后序遍历的序列。 6.13假设n和m为二叉树中两结点,用1、0或#(分别表示肯定、恰恰相反或 不一定)填写下表: 问 已知 前序遍历时 n在m前? 中序遍历时 n在m前? 后序遍历时 n在m前? n在m左方 n在m右左方 n是m祖先 n是m子孙 注:如果(1)离a和b最近的共同祖先p存在,且(2)a在p的左子树中,b 在p的右子树中,则称a在b的左方(即b在a的右方)。 6.14找出所有满足下列条件的二叉树: (a)它们在先序遍历和中序遍历时,得到的节点访问序列相同; (b)它们在后序遍历和中序遍历时,得到的结点访问序列相同; (c)它们在先序遍历和后序遍历时,得到的节点访问序列相同。 解:(a)不含左子树的二叉树。 (b)不含右子树的二叉树。 (c)即不含左子树,也不含右子树的二叉树。 6.15解: 6.16解:1112 1314 InfoABCDEFGHIJKLMN Ltag11 Lchi ld 246273111 Rtag11 Rchi ld 35658918 6.17解:其错误在于中序遍历应先访问其左子树,可做如下修改: BiTreeInSucc(BiTreeq){ //一直q是指向中序线索二叉树上某个结点的指针, //本函数返回指向*q的后继的指针。 r=q->rchild; if(!r->ltag) while(!r->ltag)r=r->lchild; returnr; }//InSucc 6.18解:如果p是根结点,则其后继为空。否则需查找p的双亲结点。从p结 点开始中序线索遍历,如果某结点的左指针域等于p,说明该结点是p的双亲结 点,且p是它的左孩子;如果某结点的右指针域等于p,说明该结点是p的双亲 结点,且p是它的右孩子;如此即可确定访问次序。若是右孩子,其后继是双亲 结点;若是左孩子,其后继是其兄弟最左下的子孙,如果兄弟不存在,其后继是 其双亲结点。 6.19分别画出和下列树对应的各个二叉树: 解: 6.20解: (1)先序:1234568791 (2)中序:348675211 (3)后序:876543211 6.23解:树的先根序列为GFKDAIEBCHJ,后根序列为DIAEKFCJHBG,可以先转化 成二叉树,再通过二叉树转换成树。注意二叉树的先根序列与等价树的先根序列 相同,二叉树的中序序列对应着树的后根序列。 GFKDAIEBCHJ为所求二叉树的先序序列,DIAEKFCJHBG为二叉树的中序序列。 通过观察先序序列,G为二叉树的根结点,再由中序序列,G的左子树序列为 DIAEKFCJHB,右子为空。可以表示成如下形式: G(DIAEKFCJHB,NULL) 对于子树先序序列为FKDAIEBCHJ,中序序列为DIAEKFCJHB,显然子树根为F。 再由中序序列可以看到,F的左子树是DIAEK,右子树为CJHB。进一步表示成: G(F(DIAEK,CJHB),NULL) 对于DIAEK(中序表示),先序为KDAIE,K为根,左子为DIAE,右子为空; 对于CJHB,B为根,左子为CJH,右子为空。进一步表示成: G(F(K(DIAE,NULL),B(CJH,NULL)),NULL) G(F(K(D(NULL,IAE),NULL),B(C(NULL,JH),NULL)),NULL) G(F(K(D(NULL,A(I,E)),NULL),B(C(NULL,H(J,NULL)),NULL)),NULL) 由此画出二叉树,进而画出树。 6.24解:本题应注意下列转换: 树森林二叉树 先根先序先序 后根中序中序 6.25解;用归纳法证明。 当n=2时,要使其成为最优二叉树,必须使两个结点都成为叶子结点。 设n=k时成立,则当n=k+1时,要使其成为最优,必须用k个结点的哈夫曼 树与第k+1个结点组成一个新的最优二叉树,所以n=k+1时也成立。 6.26解:不妨设这8个结点为A、B、C、D、E、F、G、H,其相应的权为7、19、 2、6、32、3、21、10。 A:1101B:01C:11111D:1110E:10F:11110G:00H:1100 采用这种方式编码,电文最短。 6.27解: 6.33解: intVisit(intu,intv) { if(u==v)return1; if(L[v]==0){//左子树不存在 if(R[v]==0)//右子树也不存在 return0; else{//右子树存在,继续访问右子树 if(Visit(u,R[v]))return1; elsereturn0; } } else{//左子树存在 if(Visit(u,L[v]))//左子树中存在目标 return1; else{//左子树中没有目标,需访问右子树 if(R[v]==0)//没有右子树 return0; else{//右子树存在,继续访问右子树 if(Visit(u,R[v]))return1; elsereturn0; } } } } 6.34解: intVisit(intu,intv) { intNv; Nv=NumElem(v);//返回结点v的下标 if(Nv==-1)exit(-2);//结点v不存在,退出 if(u==v)return1; if(L[Nv]==0){//左子树不存在 if(R[Nv]==0)//右子树也不存在 return0; else{//右子树存在,继续访问右子树 if(Visit(u,R[Nv]))return1; elsereturn0; } } else{//左子树存在 if(Visit(u,L[Nv]))//左子树中存在目标 return1; else{//左子树中没有目标,需访问右子树 if(R[Nv]==0)//没有右子树 return0; else{//右子树存在,继续访问右子树 if(Visit(u,R[Nv]))return1; elsereturn0; } } } } //返回结点在数组T中的下标 intNumElem(intx) { inti=0; while(i if(T[i]==x)returni; elsereturn-1; } 6.35解: #defineN8 charT[N]={'0','a','b','c','d','e','f','g'}; //用顺序数组存储,0为头结点, //a为根结点,b为左子树,c为右子树,... //若某结点编号为奇数, //则它必为其双亲的右孩子,否则为左孩子 //编号为k的结点的双亲结点的编号为k/2 intNumTree(charc);//求结点在树中的编号 intExp(inta,intb);//求a的b次方 intNumElem(charc);//返回元素在数组中的下标 intmain() { charc; cout<<"请输入结点的值:"; cin>>c; cout< return0; } intNumTree(charc)//求结点在树中的编号 { intk,i=0; intCode[N]; k=NumElem(c);//返回结点c的下标 if(k==-1)exit(-2);//结点c不存在,退出 while(k){ //如果k为偶数,记录一个代码1 if(k%2==1)Code[i]=1; elseCode[i]=0; k=k/2; i++; } intx=0,j; for(j=0;j x=x+Code[j]*Exp(2,j); returnx; } intExp(inta,intb) { inti; intx=1; for(i=0;i x=x*a; returnx; } //返回结点在数组T中的下标 intNumElem(charc) { inti=0; while(i if(T[i]==c)returni; elsereturn-1; } 6.36解: StatusSimilarTree(BiTree&T1,BiTree&T2) { if(!T1){//T1是空树 if(!T2)returnTRUE;//T2是空树 elsereturnFALSE; } else{//T1是非空树 if(!T2)returnFALSE; else{//T2是非空树 if(SimilarTree(T1->lchild,T2->lchild) &&SimilarTree(T1->rchild,T2->rchild)) returnTRUE; elsereturnFALSE; } } } 6.37解: //先序遍历的非递归算法 StatusPOTraverse(BiTree&T,Status(*Visit)(TElemTypee)) { BiTreep; Stacks; InitStack(s); p=T; while(p||!StackEmpty(s)){ if(p){ //如果根指针不空,访问根结点, //右指针域压栈,继续遍历左子树 if(!Visit(p->data))returnERROR; Push(s,p->rchild); p=p->lchild; }//根指针已空,本子树已遍历完毕, //退栈返回上一层,继续遍历未曾访问的结点 elsePop(s,p); } returnOK; } 6.41解: //求位于先序序列中第k个位置的结点的值, //e中存放结点的返回值,i为计数器 StatusPONodeK(TElemType&e,int&i,intk,BiTree&T) { if(T){ i++; if(i==k)e=T->data; else{ PONodeK(e,i,k,T->lchild); PONodeK(e,i,k,T->rchild); } } returnOK; } 6.42解: //求二叉树中叶子结点的数目 StatusPOLeafNodeNum(int&i,BiTree&T) { if(T){ if(!T->lchild&&!T->rchild)i++; POLeafNodeNum(i,T->lchild); POLeafNodeNum(i,T->rchild); } returnOK; } 6.43解: //按先序交换二叉树的左右子树 StatusExchangeBiTree(BiTree&T) { BiTreep; if(T){ p=T->lchild; T->lchild=T->rchild; T->rchild=p; ExchangeBiTree(T->lchild); ExchangeBiTree(T->rchild); } returnOK; } 6.44解: //求二叉树中以元素值为x的结点为根的子树的深度 StatusChildTreeDepth(BiTree&T,TElemTypex,int&depth) { BiTreeT1; if(PreOrderLocate(T,x,T1)){ depth=BiTDepth(T1); returnOK; } elsereturnERROR; } //按先序在树中查找根为x的子树,T1为指向子树根的指针 StatusPreOrderLocate(BiTree&T,TElemTypex,BiTree&T1) { if(T){ if(T->data==x){ T1=T; returnOK; } else{ if(PreOrderLocate(T->lchild,x,T1)) returnOK; else{ if(PreOrderLocate(T->rchild,x,T1)) returnOK; elsereturnERROR; } } } elsereturnERROR; } //求二叉树的深度 intBiTDepth(BiTree&T) { intldep,rdep; if(!T)return0; else{ ldep=BiTDepth(T->lchild)+1; rdep=BiTDepth(T->rchild)+1; returnldep>rdep?ldep:rdep; } } 6.45解: //删除以元素值为x的结点为根的子树 StatusDelChildTree(BiTree&T,TElemTypex) { if(T){ if(T->data==x){ DelBTree(T); T=NULL; returnOK; } else{ if(DelChildTree(T->lchild,x)) returnOK; else{ if(DelChildTree(T->rchild,x)) returnOK; elsereturnERROR; } } } elsereturnERROR; } //删除二叉树 StatusDelBTree(BiTree&T) { if(T){ DelBTree(T->lchild); DelBTree(T->rchild); deleteT; returnOK; } elsereturnERROR; } 6.46解: //复制一棵二叉树 StatusCopyBiTree(BiTree&T,BiTree&T1) { BiTreep; if(T){ p=newBiTNode; if(!p)returnERROR; p->data=T->data; T1=p; CopyBiTree(T->lchild,T1->lchild); CopyBiTree(T->rchild,T1->rchild); } else{ T1=T; } returnOK; } 6.47解: typedefBiTreeQElemType; #include"c:YinincludeQueue.h" StatusLevelOrderTraverse(BiTree&T,Status(*Visit)(TElemTypee)) { QElemTypep; Queueq; InitQueue(q); if(T)EnQueue(q,T); while(!QueueEmpty(q)){ DeQueue(q,p); Visit(p->data); if(p->lchild)EnQueue(q,p->lchild); if(p->rchild)EnQueue(q,p->rchild); } returnOK; } 6.48解: //在二叉树T中求结点*p和*q的共同最小祖先e StatusMinComAncst(BiTree&T,TElemType&e,TElemType*p,TElemType*q) { if(!T)returnERROR; BiTreeT1,T2,pt=NULL; if(!CopyBiTree(T,T1))returnERROR; if(!CopyBiTree(T,T2))returnERROR; if(!PathTree(T1,p)) returnERROR;//求根结点到结点p的路径树T1 elseShowBiTree(T1); cout< if(!PathTree(T2,q)) returnERROR;//求根结点到结点q的路径树T2 elseShowBiTree(T2); cout< while(T1&&T2&&T1->data==T2->data){ pt=T1; if(T1->lchild){ T1=T1->lchild; T2=T2->lchild; } else{ if(T1->rchild){ T1=T1->rchild; T2=T2->rchild; } } } if(!pt)returnERROR; else{ e=pt->data; returnOK; } } //在二叉树T中求根到结点p的路径树,该操作将剪去除路径之外的所有分支 StatusPathTree(BiTree&T,TElemType*p) { if(!T||!p)returnERROR; if(T->data==*p){//找到目标,删除目标的左右子树 if(T->lchild)DelBiTree(T->lchild); if(T->rchild)DelBiTree(T->rchild); returnOK; } else{//没找到目标,继续递归查找 if(PathTree(T->lchild,p)){//目标在左子树中,删除右子树 if(T->rchild)DelBiTree(T->rchild); returnOK; } else if(PathTree(T->rchild,p)){//目标在右子树中,删除左子树 if(T->lchild)DelBiTree(T->lchild); returnOK; } elsereturnERROR;//找不到目标 } } 6.49解: StatusCompleteBiTree(BiTree&T) { intd; if(T){ d=BiTDepth(T->lchild)-BiTDepth(T->rchild); if(d1)returnERROR; else{ if(CompleteBiTree(T->lchild)&& CompleteBiTree(T->rchild))returnOK; elsereturnERROR; } } elsereturnOK; } 6.51解: StatusShowBiTExpress(BiTree&T) { if(T){ if(T->lchild){ if(Low(T->lchild->data,T->data)){ cout<<'('; ShowBiTExpress(T->lchild); cout<<')'; } elseShowBiTExpress(T->lchild); } cout if(T->rchild){ if(Low(T->rchild->data,T->data)){ cout<<'('; ShowBiTExpress(T->rchild); cout<<')'; } elseShowBiTExpress(T->rchild); } } returnOK; } StatusLow(chara,charb) { if((a=='+'||a=='-')&&(b=='*'||b=='/'))returnTRUE; elsereturnFALSE; } 6.52解: intBiTreeThrive(BiTree&T) { inti,d,nn[20]; d=BiTDepth(T); BiTreep=T; Stacks1,s2; InitStack(s1);InitStack(s2); for(i=0;i<20;i++){ nn[i]=0;//每层结点个数 } if(p)Push(s1,p); elsereturn0; for(i=0;i if(!StackEmpty(s1)&&StackEmpty(s2)){ while(!StackEmpty(s1)){ Pop(s1,p);nn[i]++;//s1中存放第i层的结点 if(p->lchild)Push(s2,p->lchild);//s2中存放第i+1层结点 if(p->rchild)Push(s2,p->rchild); } } else{ if(StackEmpty(s1)&&!StackEmpty(s2)){ while(!StackEmpty(s2)){ Pop(s2,p);nn[i]++; if(p->lchild)Push(s1,p->lchild); if(p->rchild)Push(s1,p->rchild); } } } } intmax=nn[0]; for(i=0;i if(max returnmax*d; } 6.53解: //所有从根到叶子最长路径树 StatusMaxPathBiTree(BiTree&T) { if(T){ if(BiTDepth(T)-BiTDepth(T->lchild)!=1) DelBiTree(T->lchild); else MaxPathBiTree(T->lchild); if(BiTDepth(T)-BiTDepth(T->rchild)!=1) DelBiTree(T->rchild); else MaxPathBiTree(T->rchild); } returnOK; } //从根到叶子最长路径中最左方的路径树 StatusLMaxPathBiTree(BiTree&T) { if(T){ if(BiTDepth(T)-BiTDepth(T->lchild)==1){ DelBiTree(T->rchild); LMaxPathBiTree(T->lchild); } else{ DelBiTree(T->lchild); if(BiTDepth(T)-BiTDepth(T->rchild)==1) LMaxPathBiTree(T->rchild); else DelBiTree(T->rchild); } } returnOK; } 6.54解: //根据完全二叉顺序树创建完全二叉链表树 StatusCreateCompleteBiTree(SqList&ST,BiTree<) { BiTreep; inti=0,len; if(==0)returnOK; p=newBiTNode; if(!p)returnERROR; p->data=(i); p->lchild=NULL; p->rchild=NULL; LT=p; Queueq; InitQueue(q); EnQueue(q,p); len=(); while(!QueueEmpty(q)&&i DeQueue(q,p); if(i p->lchild=newBiTNode; if(!p->lchild)returnERROR; p->lchild->data=(++i); p->lchild->lchild=NULL; p->lchild->rchild=NULL; EnQueue(q,p->lchild); } if(i p->rchild=newBiTNode; if(!p->rchild)returnERROR; p->rchild->data=(++i); p->rchild->lchild=NULL; p->rchild->rchild=NULL; EnQueue(q,p->rchild); } } returnOK; } 6.55解: StatusPreOrderTraverse(BiTree&T) { if(T){ T->DescNum=DescendNum(T); PreOrderTraverse(T->lchild); PreOrderTraverse(T->rchild); } returnOK; } intDescendNum(BiTree&T) { if(!T)return0; if(!T->lchild){ if(!T->rchild)return0; elsereturnDescendNum(T->rchild)+1; } else{ if(!T->rchild)returnDescendNum(T->lchild)+1; elsereturnDescendNum(T->rchild)+DescendNum(T->lchild)+2; } } 6.56解: 先对二叉树T进行先序线索,得到先序线索二叉树Thrt。然后再进行查找。 //先序线索二叉树算法 StatusPreOrderThreading(BiThrTree&Thrt,BiThrTree&T) { BiThrTreepre; Thrt=newBiThrNode;//为线索二叉树建立头结点 if(!Thrt)exit(OVERFLOW); Thrt->LTag=Thread; Thrt->RTag=Link; Thrt->lchild=Thrt;//左子树回指 if(!T)Thrt->rchild=Thrt;//若二叉树空,右子树回指 else{ Thrt->rchild=T; pre=Thrt; PreThreading(T,pre);//先序遍历进行先序线索化 pre->rchild=Thrt;//最后一个结点线索化 pre->RTag=Thread; Thrt->lchild=pre; } returnOK; } StatusPreThreading(BiThrTree&T,BiThrTree&pre) { if(T){ if(!T->lchild){ T->LTag=Thread; T->lchild=pre; } if(pre&&!pre->rchild){ pre->RTag=Thread; pre->rchild=T; } pre=T; if(T->LTag==Link)PreThreading(T->lchild,pre); if(T->RTag==Link)PreThreading(T->rchild,pre); } returnOK; } //从二叉线索树上任一结点q开始查找结点*p。 //如果找到,将*p的后继结点指针存于q中,返回TRUE;否则返回FALSE StatusFindNextInBiThrTree(BiThrTree&q,TElemType*p) { BiThrTreept=q; if(!pt)returnFALSE;