admin 管理员组

文章数量: 887021


2023年12月17日发(作者:setcommstate)

第1章绪论

1.1简述下列术语:数据,数据元素、数据对象、数据结构、存储结构、数据类型和抽象数据类型。

解:数据是对客观事物的符号表示。在计算机科学中是指所有能输入到计算机中并被计算机程序处理的符号的总称。

数据元素是数据的基本单位,在计算机程序中通常作为一个整体进行考虑和处理。

数据对象是性质相同的数据元素的集合,是数据的一个子集。

数据结构是相互之间存在一种或多种特定关系的数据元素的集合。

存储结构是数据结构在计算机中的表示。

数据类型是一个值的集合和定义在这个值集上的一组操作的总称。

抽象数据类型是指一个数学模型以及定义在该模型上的一组操作。是对一般数据类型的扩展。

1.2试描述数据结构和抽象数据类型的概念与程序设计语言中数据类型概念的区别。

解:抽象数据类型包含一般数据类型的概念,但含义比一般数据类型更广、更抽象。一般数据类型由具体语言系统内部定义,直接提供给编程者定义用户数据,因此称它们为预定义数据类型。抽象数据类型通常由编程者定义,包括定义它所使用的数据和在这些数据上所进行的操作。在定义抽象数据类型中的数据部分和操作部分时,要求只定义到数据的逻辑结构和操作说明,不考虑数据的存储结构和操作的具体实现,这样抽象层次更高,更能为其他用户提供良好的使用接口。

1.3设有数据结构(D,R),其中

Dd1,d2,d3,d4,Rr,rd1,d2,d2,d3,d3,d4

试按图论中图的画法惯例画出其逻辑结构图。

解:

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

n(n1)(4)n+(n-1)+(n-2)+...+1=

2(5)1+(1+2)+(1+2+3)+...+(1+2+3+...+n)=i1ni(i1)

21n1n21n21n=i(i1)(ii)ii

2i12i12i12i1111n(n1)(2n1)n(n1)n(n1)(2n3)

12412(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;

解:o(log2n)

count=log2n2

1.11已知有实现同一功能的两个算法,其时间复杂度分别为O2n和On10,假设现实计算机可连续运算的时间为107秒(100多天),又每秒可执行基本操作(根据这些操作来估算算法时间复杂度)105次。试问在此条件下,这两个算法可解问题的规模(即n值的范围)各为多少?哪个算法更适宜?请说明理由。

解:2n1012

n101012

n=40

n=16

则对于同样的循环次数n,在这个规模下,第二种算法所花费的代价要大得多。故在这个规模下,第一种算法更适宜。

1.12设有以下三个函数:

fn21n4n21000,gn15n4500n3,hn500n3.5nlogn

请判断以下断言正确与否:

(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值,比较两函数n2和50nlog2n的增长趋势,并确定n在什么范围内,函数n2的值大于50nlog2n的值。

解:n2的增长趋势快。但在n较小的时候,50nlog2n的值较大。

当n>438时,n250nlog2n

1.14判断下列各对函数fn和gn,当n时,哪个函数增长更快?

(1)fn10n2lnn!10n,gn2n4n7

3(2)fnlnn!5,gn13n2.5

2

(3)fnn2.1n41,gnlnn!n

2232(4)fn2n2n,gnnnn5

解:(1)g(n)快(2)g(n)快(3)f(n)快(4)f(n)快

1.15试用数学归纳法证明:

(1)i2nn12n1/6

i1nnn0

x1,n0 (2)xixn11/x1

i0n(3)2i12n1

i1nn1

n1 (4)2i1n2

i11.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阶斐波那契序列的定义为

f00,f10,…,fk20,fk11;

fnfn1fn2fnk,nk,k1,

试编写求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试编写算法,计算i!*2i的值并存入数组a[0..arrsize-1]的第i-1个分量中(i=1,2,…,n)。假设计算机中允许的整数最大值为maxint,则当n>arrsize或对某个k1kn,使k!2kmaxint时,应按出错处理。注意选择你认为较好的出错处理方法。

解:

#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试编写算法求一元多项式的值Pnxaixi的值Pnx0,并确定算法中每i0n一语句的执行次数和整个算法的时间复杂度。注意选择你认为较好的输入和输出方法。本题的输入为aii0,1,,n,x0和n,输出为Pnx0。

解:

#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<=n;i++)cin>>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<0||i+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<0||i>-1||k<0||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设Aa1,,am和Bb1,,bn均为顺序表,A和B分别为A和B中除去最大共同前缀后的子表。若AB空表,则AB;若A=空表,而B空表,或者两者均不为空表,且A的首元小于B的首元,则AB;否则AB。试写一个比较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,intj,intlen)

{

if(i<0||j<0||len<0)returnINFEASIBLE;

p=la; k=1;

while(knext; k++; }

q=p;

while(k<=len){ q=q->next; k++; }

s=lb;k=1;

while(knext; k++; }

s->next=p; q->next=s->next;

returnOK;

}

解:

StatusDeleteAndInsertSub(LinkList&la,LinkList&lb,inti,intj,intlen)

{

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试写一算法,实现顺序表的就地逆置,即利用原表的存储空间将线性表a1,,an逆置为an,,a1。

解:

//顺序表的逆置

StatusListOppose_Sq(SqList&L)

{

inti;

ElemTypex;

for(i=0;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;

Bb1,b2,,bn,2.23设线性表Aa1,a2,,am,试写一个按下列规则合并A,B为线性表C的算法,即使得

Ca1,b1,,am,bm,bm1,,bn 当mn时;

Ca1,b1,,an,bn,an1,,am 当mn时。

线性表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定义为:

typedef struct XorNode{

chardata;

struct XorNode*LRPtr;

}XorNode,*XorPointer;

typede struct{ //无头结点的异或指针双向链表

XorPointer Left,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设以带头结点的双向循环链表表示的线性表La1,a2,,an。试写一时间复杂度O(n)的算法,将L改造为La1,a3,,an,,a4,a2。

解:

//将双向链表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定义为

typedef struct{

int coef;

intexp;

}PolyTerm;

typedefstruct{ //多项式的顺序存储结构

PolyTerm*data;

intlast;

}SqPoly;

2.39已知稀疏多项式Pnxc1xe1c2xe2cmxem,其中nemem1e10,ci0i1,2,,m,m1。试采用存储量同多项式项数m成正比的顺序存储结构,编写求Pnx0的算法(x0为给定值),并分析你的算法的时间复杂度。

解:

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题给定的条件和存储结构,编写求PxPn1xPn2x的算法,将结果多项式存放在新辟的空间中,并分析你的算法的时间复杂度。

解:

//求两多项式的差

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得到的输出序列为p1p2pn(它是输入序列的一个排列),则在输出序列中不可能出现这样的情形:存在着i

解:这个问题和3.1题比较相似。因为输入序列是从小到大排列的,所以若

pj

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^FPUSH(OPND,A)

#

2 # A -B*C/D+E^F# PUSH(OPTR,-)

3 #- A B*C/D+E^F# PUSH(OPND,B)

4 #- AB *C/D+E^F# PUSH(OPTR,*)

5 #-* AB C/D+E^F# PUSH(OPND,C)

6 #-* ABC /D+E^F# Operate(B,*,C)

7 #- AG /D+E^F# PUSH(OPTR,/)

8 #-/ AG D+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 #+ I E^F# PUSH(OPND,E)

13 #+ IE ^F# PUSH(OPTR,^)

14 #+^ IE F# PUSH(OPND,F)

15 #+^ IEF # Operate(E,^,F)

16 #+ IJ # Operate(I,+,J)

17 # K # RETURN

3.8试推导求解n阶梵塔问题至少要执行的move操作的次数。

解:2n1

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的整数。编写算法置换点(i0,j0)所在区域的颜色。约定和(i0,j0)同色的上、下、左、右的邻接点为同色区域的点。

解:

#include

#include

typedefstruct{

intx;

inty;

}PosType;

typedefstruct{

intColor;

intVisited;

PosTypeseat;

}ElemType;

#include"d:VC99Stack.h"

#defineM 8

#defineN 8

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);


本文标签: 结点 算法 序列 元素 操作