《C语言版 第二版 (陈守孔 孟佳娜 武秀川 著) 机械工业出版社答案.doc》由会员分享,可在线阅读,更多相关《C语言版 第二版 (陈守孔 孟佳娜 武秀川 著) 机械工业出版社答案.doc(83页珍藏版)》请在装配图网上搜索。
第1章 绪论
一、基础知识题
1. 简述下列概念
数据,数据元素,数据类型,数据结构,逻辑结构,存储结构,算法。
【解答】数据是信息的载体,是描述客观事物的数、字符,以及所有能输入到计算机中并被计算机程序识别和处理的符号的集合。数据元素是数据的基本单位。在不同的条件下,数据元素又可称为元素、结点、顶点、记录等。
数据类型是对数据的取值范围、数据元素之间的结构以及允许施加操作的一种总体描述。每一种计算机程序设计语言都定义有自己的数据类型。
“数据结构”这一术语有两种含义,一是作为一门课程的名称;二是作为一个科学的概念。作为科学概念,目前尚无公认定义,一般认为,讨论数据结构要包括三个方面,一是数据的逻辑结构,二是数据的存储结构,三是对数据进行的操作(运算)。而数据类型是值的集合和操作的集合,可以看作是已实现了的数据结构,后者是前者的一种简化情况。
数据的逻辑结构反映数据元素之间的逻辑关系(即数据元素之间的关联方式或“邻接关系”),数据的存储结构是数据结构在计算机中的表示,包括数据元素的表示及其关系的表示。数据的运算是对数据定义的一组操作,运算是定义在逻辑结构上的,和存储结构无关,而运算的实现则依赖于存储结构。
数据结构在计算机中的表示称为物理结构,又称存储结构。是逻辑结构在存储器中的映像,包括数据元素的表示和关系的表示。逻辑结构与计算机无关。
算法是对特定问题求解步骤的一种描述,是指令的有限序列。其中每一条指令表示一个或多个操作。一个算法应该具有下列特性:有穷性、确定性、可行性、输入和输出。
2. 数据的逻辑结构分哪几种,为什么说逻辑结构是数据组织的主要方面?
【解答】数据的逻辑结构分为线性结构和非线性结构。(也可以分为集合、线性结构、树形结构和图形即网状结构)。
逻辑结构是数据组织的某种“本质性”的东西:
(1)逻辑结构与数据元素本身的形式、内容无关。
(2)逻辑结构与数据元素的相对位置无关。
(3)逻辑结构与所含数据元素的个数无关。
3. 试举一个数据结构的例子,叙述其逻辑结构、存储结构、运算三方面的内容。
【解答】学生成绩表,逻辑结构是线性结构,可以顺序存储(也可以链式存储),运算可以有插入、删除、查询、等等。
4. 简述算法的五个特性,对算法设计的要求。
【解答】算法的五个特性是:有穷性、确定性、可行性、零至多个输入和一至多个输出。
对算法设计的要求:正确性,易读性,健壮性,和高的时空间效率(运算速度快,存储空间小)。
5. 设n是正整数,求下列程序段中带@记号的语句的执行次数。
(1)i=1;k=0; (2) i=1;j=0;
while(i
j)j++; @
} else i++; } @
(3)x=y=0; (4)x=91;y=100;
for(i=0;i0)
for(j=0;j100)
{x++; @ {x=x-10; y--; @
for(k=0;i4时,算法A2好于A1。
7. 选择题:算法分析的目的是( )
A、找出数据结构的合理性 B、研究算法中的输入和输出的关系
C、分析算法的效率以求改进 D、分析算法的易懂性和文档特点
【解答】C
二、算法设计题
8. 已知输入x,y,z三个不相等的整数,设计一个“高效”算法,使得这三个数按从小到大输出。“高效”的含义是用最少的元素比较次数、元素移动次数和输出次数。
void Best()
{//按序输出三个整数的优化算法
int a,b,c,t;
scanf(“%d%d%d”,&a,&b,&c);
if(a>b)
{t=a; a=b; b=t:} //a和b已正序
if(b>c)
{t=c; c=b; //c已到位
if(a>t) {b=a; a=t;} //a和b已正序
else b=t;
}
printf(“%d,%d,%d\n”,a,b,c);
//最佳2次比较,无移动;最差3次比较,7个赋值
}
9. 在数组A[n]中查找值为k的元素,若找到则输出其位置i(1≤i≤n),否则输出0作为标志。设计算法求解此问题,并分析在最坏情况下的时间复杂度。
【题目分析】从后向前查找,若找到与k值相同的元素则返回其位置,否则返回0。
int Search(ElemType A[n+1], ElemType k)
{i=n;
wile(i>=1)&&(A[i]!=k)) i--;
if(i>=1) return i;
else return 0;
}
当查找不成功时,总的比较次数为n+1次,所以最坏情况下时间复杂度为O(n)。
第2章 线性表
一、基础知识题
2.1 试述头指针、头结点、元素结点、首元结点的区别,说明头指针和头结点的作
【解答】指向链表第一个结点(或为头结点或为首元结点)的指针称为头指针。“头指针”具有标识一个链表的作用,所以经常用头指针代表链表的名字,如链表L既是指链表的名字是L,也是指链表的第一个结点的地址存储在指针变量L中,头指针为“NULL”则表示一个空表。
有时,我们在整个线性链表的第一个元素结点之前加入一个结点,称为头结点,它的数据域可以不存储任何信息(也可以做监视哨或存放线性表的长度等附加信息),指针域中存放的是第一个数据结点的地址,空表时为空。 “头结点”的加入,使插入和删除等操作方便统一。
元素结点即是数据结点,至少包括元素自身信息和其后继元素的地址两个域。
首元结点是指链表中第一个数据元素的结点;为了操作方便,通常在链表的首元结点之前附设一个结点,称为头结点。
2.2分析顺序存储结构和链式存储结构的优缺点,说明何时应该利用何种结构。
【解答】①从空间上来看,当线性表的长度变化较大,难以估计其规模时,选用动态的链表作为存储结构比较合适,但链表除了需要设置数据域外,还要额外设置指针域,因此当线性表长度变化不大,易于事先确定规模时,为了节约存储空间,宜采用顺序存储结构。
②从时间上看,顺序表具有按元素序号随机访问的特点,在顺序表中按序号访问数据元素的时间复杂度为O(1);而链表中按序号访问的时间复杂度为O(n)。所以如果经常按序号访问数据元素,使用顺序表优于链表。
在顺序表中做插入删除操作时,平均移动大约表中一半的元素,因此n较大时顺序表的插入和删除效率低。在链表中作插入、删除,虽然也要找插入位置,但操作主要是比较操作。从这个角度考虑显然链表优于顺序表。
总之,两种存储结构各有长短,选择那一种存储结构,由实际问题中的主要因素决定。
2.3 分析在顺序存储结构下插入和删除结点时平均需要移动多少个结点。
【解答】平均移动表中大约一半的结点,插入操作平均移动 个结点,删除操作平均移动 个结点。具体移动的次数取决于表长和插入、删除的结点的位置。
2.4 为什么在单循环链表中常使用尾指针,若只设头指针,插入元素的时间复杂度如何?
【解答】单循环链表中无论设置尾指针还是头指针都可以遍历表中任一个结点。设置尾指针时,若在表尾进行插入元素或删除第一元素,操作可在O(1)时间内完成;若只设置头指针,表尾进行插入或删除操作,需要遍历整个链表,时间复杂度为O(n)。
2.5 在单链表、双链表、单循环链表中,若知道指针p指向某结点,能否删除该结点,时间复杂度如何?
【解答:】以上三种链表中,若知道指针p指向某结点,都能删除该结点。双链表删除p所指向的结点的时间复杂度为O(1),而单链表和单循环链表上删除p所指向的结点的时间复杂度均为O(n)。
2.6 下面算法的功能是什么?
LinkedList Unknown(LinkedList la)
{LNode *q,*p;
if(la && la->next)
{q=la; la=la->next; p=la;
while(p->next) p=p->next;
p->next=q; q->next=null;
}
return la;
}
【解答】将首元结点删除并插入到表尾(设链表长度大于1)。
2.7 选择题:在循环双链表的*p结点之后插入*s结点的操作是( )
la、p->next=s; s->prior=p; p->next->prior=s; s->next=p->next;
B、p->next=s; p->next->prior=s; s->prior=p; s->next=p->next;
lc、s->prior=p; s->next=p->next; p->next:=s; p->next->prior=s;
D、s->prior=p; s>next=p>next; p>next->prior =s; p->next=s;
【解答】D
2.8 选择题:若某线性表最常用的操作是存取任一指定序号的元素和在最后进行插入和删除运算,则利用( )存储方式最节省时间。
la.顺序表 B.双链表 lc.带头结点的双循环链表 D.单循环链表
【解答】la
二、算法设计题
2.9 设ha和hb分别是两个带头结点的非递减有序单链表的头指针,试设计算法, 将这两个有序链表合并成一个非递增有序的单链表。要求使用原链表空间,表中无重复数据。
【题目分析】因为两链表已按元素值非递减次序排列,将其合并时,均从第一个结点起进行比较,将小的链入链表中,同时后移链表工作指针,若遇值相同的元素,则删除之。该问题要求结果链表按元素值非递增次序排列,故在合并的同时,将链表结点逆置。
LinkedList Union(LinkedList ha,hb)
∥ha,hb分别是带头结点的两个单链表的头指针,链表中的元素值按递增序排列
∥本算法将两链表合并成一个按元素值递减次序排列的单链表,并删除重复元素
{ pa=ha->next; ∥pa是链表ha的工作指针
pb=hb->next; ∥pb是链表hb的工作指针
ha->next=null; ∥ha作结果链表的头指针,先将结果链表初始化为空
while(pa!=null && pb!=null) ∥当两链表均不为空时作
{while(pa->next && pa->data==pa->next->data)
{u=pa->next; pa->next=u->next; free(u)}∥删除pa链表中的重复元素
while(pb->next && pb->data==pb->next->data)
{u=pb->next; pb->next=u->next; free(u)}∥删除pb链表中的重复元素
if(pa->datadata)
{r=pa->next; ∥将pa 的后继结点暂存于r
pa->next=ha->next; ∥将pa结点链于结果表中,同时逆置
ha->next=pa;
pa=r; ∥恢复pa为当前待比较结点
}
else if(pb->datadata)
{r=pb->next; ∥ 将pb 的后继结点暂存于r
pb->next=ha->next; ∥将pb结点链于结果表中,同时逆置
ha->next=pb;
pb=r; ∥恢复pb为当前待比较结点
}
else{u=pb;pb=pb->next;free(u)}∥删除链表pb和pa中的重复元素
}// while(pa!=null && pb!=null)
if(pa) pb=pa; ∥避免再对pa写下面的while语句
while(pb!=null) ∥将尚未到尾的表逆置到结果表中
{r=pb->next; pb->next=ha->next; ha->next=pb; pb=r; }
return ha
}∥算法Union结束
2.10 设la是一个双向循环链表,其表中元素递增有序。试写一算法插入元素x,使表中元素依然递增有序。
【问题分析】双向链表的插入与单链表类似,但需修改双向指针。
DLinkedList DInsert(DLinkedList la, ElemType x)
∥在递增有序的双向循环链表la中插入元素x,使表中元素依然递增有序
{p=la->next; ∥p指向第一元素
la->data=MaxElemType;∥MaxElemType是和x同类型的机器最大值,用做监视哨
while(p->datanext;
s=(DLNode *)malloc(sizeof(DLNode));∥申请结点空间
s->data=x;
s->prior=p->prior; s->next=p; ∥将插入结点链入链表
p->prior->next=s; p->prior=s;
}
2.11 设p指向头指针为la的单链表中某结点,试编写算法,删除结点*p的直接前驱结点。
【题目分析】设*p是单链表中某结点,删除结点*p的直接前驱结点,要找到*p的前驱结点的前驱*pre。进行如下操作:u=pre->next; pre->next=u->next;free(u);
LinkedList LinkedListDel(LinkedList la,LNode *p)
{∥删除单链表la上的结点*p的直接前驱结点,假定*p存在
pre=la;
if(pre-next==p)
printf(“*p是链表第一结点,无前驱\n”); exit(0); }
while(pre->next->next !=p)
pre=pre->next;
u=pre->next; pre->next=u->next; free(u);
return(la);
}
2.12 设计一算法,将一个用循环链表表示的稀疏多项式分解成两个多项式,使这两个多项式各自仅有奇次幂或偶次幂项,并要求利用原链表中的结点空间来构造这两个链表。
【题目分析】设循环链表表示的多项式的结点结构为:
typedef struct node
{int power; ∥幂
float coef; ∥系数
ElemType other; ∥其他信息
struct node *next; ∥指向后继的指针
}PNode,*PolyLinkedList;
则可以从第一个结点开始,根据结点的幂是奇数或偶数而将其插入到奇次幂或偶次幂项的链表中。假定用原链表保存偶次幂,要为奇次幂的链表生成一个表头,为了保持链表中结点的原来顺序,用一个指针指向奇次幂链表的表尾,注意链表分解时不能“断链”。
void PolyDis(PolyLinkedList poly)
∥将poly表示的多项式链表分解为各含奇次幂或偶次幂项的两个循环链表
{PolyLinkedList poly2=(PolyLinkedList)malloc(sizeof(PNode));
∥poly2表示只含奇次幂的多项式
r2=poly2; ∥r2是只含奇次幂的多项式链表的尾指针
r1=poly; ∥r1是只含偶次幂的多项式链表当前结点的前驱结点的指针
p=poly->next; ∥链表带头结点,p指向第一个元素
while(p!=poly)
if(p->power % 2)∥处理奇次幂
{r=p->next; ∥暂存后继
r2->next=p; ∥结点链入奇次幂链表
r2=p; ∥尾指针后移
p=r; ∥恢复当前待处理结点
}
else ∥处理偶次幂
{r1->next=p; r1=p; p=p->next;}
}
r->next=poly2; r1->next=poly; ∥构成循环链表
}∥PolyDis
2.13 以带头结点的双向链表表示的线性表L=(a1,a2,…,an),试写一时间复杂度为O(n)的算法,将L改造为L=(a1,a3,…,an,…,a4,a2)。
【题目分析】分析结果链表,易见链表中位置是奇数的结点保持原顺序,而位置是偶数的结点移到奇数结点之后,且以与原来相反的顺序存放。因此,可从链表第一个结点开始处理,位置是奇数的结点保留不动,位置是偶数的结点插入到链表尾部,并用一指针指向链表尾,以便对偶数结点“尾插入”。
DLinkedList DInvert(DLinkedList L)
∥将双向循环链表L位置是偶数的结点逆置插入到链表尾部
{p=L->next; ∥p指向第一元素
Q=p->prior; ∥Q指向最后一个元素
pre=L; ∥pre指向链表中位置为奇数的结点的前驱
r=L; ∥r指向链表中偶数结点的尾结点
i=0; ∥i记录结点序号
while(p!= Q) ∥寻找插入位置
{i++;
if(i%2) ∥处理序号为奇数的结点
{p->prior=pre;pre->next=p;pre=p; p=p->next;}
else ∥处理序号为偶数的结点
{u=p; ∥记住当前结点
p=p->next;∥p指向下个待处理结点
u->prior=r->prior;∥以下4个语句将结点插入链表尾
u->next=r;
r->prior->next=u;
r->prior=u;
r=u; ∥指向新的表尾
}
}
2.14 设单向链表的头指针为head,试设计算法,将链表按递增的顺序就地排序。
【题目分析】本题中的“就地排序”,可理解为不另辟空间,这里利用直接插入原则把链表整理成递增有序链表。
LinkedList LinkListInsertSort(LinkedList head)
∥head是带头结点的单链表,本算法利用直接插入原则将链表整理成递增的有序链表
{if(head->next!=null) ∥链表不为空表
{p=head->next->next; ∥p指向第一结点的后继
head->next->next=null;
∥直接插入原则认为第一元素有序,然后从第二元素起依次插入
while(p!=null)
{r=p->next; ∥暂存p的后继
q=head;
while(q->next && q->next->datadata)
q=q->next;∥查找插入位置
p->next=q->next; ∥将p结点链入链表
q->next=p;
p=r;
}
} }
2.15 已知递增有序的三个单链表分别代表集合A,B和C,设计算法实现A=A∪(B∩C),并使结果链表仍保持递增。要求算法的时间复杂度为O(|A|+|B|+|C|)。其中,|A|为集合A的元素个数。
【题目分析】本题首先求B和C的交集,即求B和C中共有元素,再与A求并集,同时删除重复元素,以保持结果A递增。
LinkedList union(LinkedList A,B,C)
∥A、B和C均是带头结点的递增有序的单链表,本算法实现A=A∪(B∩C)
∥使结果表A保持递增有序
{pa=A->next;pb=B->next;pc=C->next;∥设置三个工作指针
pre=A; ∥pre指向结果链表中当前待合并结点的前驱
A->data=MaxElemType;∥同类型元素最大值,起监视哨作用
while(pa || pb && pc)
{while(pb && pc)
if(pb->datadata) pb=pb->next;
else if(pb->data>pc->data) pc=pc->next;
else break; ∥B表和C表有公共元素
if(pb && pc)
{while(pa && pa->datadata)∥先将A中小于B,C公共元素部分链入
{pre->next=pa;pre=pa;pa=pa->next;}
if(pre->data!=pb->data)
{pre->next=pb;pre=pb;pb=pb->next;pc=pc->next;}
else{pb=pb->next;pc=pc->next;}
∥ 若A中已有B,C公共元素,则不再存入结果表
}
}∥ while(pa||pb&&pc)
if(pa) pre->next=pa;
else pre->next=null; ∥当B,C无公共元素,将A中剩余链入
}∥算法Union结束
2.16 顺序表la与lb非递减有序,顺序表空间足够大。试设计一种高效算法,将lb中元素合到la中,使新的la的元素仍保持非递减有序。高效指最大限度地避免移动元素。
【题目分析】顺序存储结构的线性表的插入,其时间复杂度为O(n),平均移动近一半的元素。线性表la和lb合并时,若从第一个元素开始,一定会造成元素后移,这不符合本题“高效算法”的要求。应从线性表的最后一个元素开始比较,大者放到最终位置上。设两线性表的长度各为m和n ,则结果表的最后一个元素应在m+n位置上。这样从后向前,直到第一个元素为止。
SeqList Union(SeqList la, SeqList lb)
∥la和lb是顺序存储的非递减有序线性表,本算法将lb合并到la中,元素仍非递减有序
{ m=la.last;n=lb.last;∥m,n分别为线性表la和lb的长度
k=m+n-1; ∥k为结果线性表的工作指针(下标)
i=m-1;j=n-1; ∥i,j分别为线性表la和lb的工作指针(下标)
while(i>=0 && j>=0)
if(la.data[i]>=lb.data[j]) la.data[k--]=la.data[i--];
else la.data[k--]=lb.data[j--];
while(j>=0) la.data[k--]=lb.data[j--];
la.last=m+n;
return la;
}
【算法讨论】算法中数据移动是主要操作。在最佳情况下(lb的最小元素大于la的最大元素),仅将lb的n个元素移(拷贝)到la中,时间复杂度为O(n),最差情况,la的所有元素都要移动,时间复杂度为O(m+n)。因数据合并到la中,所以在退出第一个while循环后,只需要一个while循环,处理lb中剩余元素。第二个循环只有在lb有剩余元素时才执行,而在la有剩余元素时不执行。本算法 “最大限度的避免移动元素”,是“一种高效算法”。
2.17 已知非空线性链表由head指出,试写一算法,将链表中数据域值最小的那个结点移到链表的最前面。要求:不得额外申请新的链结点。
【题目分析】 本题要求将链表中数据域值最小的结点移到链表的最前面。首先要查找最小值结点。将其移到链表最前面,实质上是将该结点从链表上摘下(不是删除并回收空间),再插入到链表的最前面。
LinkedList Delinsert(LinkedList head)
∥本算法将非空线性链表head中数据域值最小的那个结点移到链表的最前面
{p=head->next;∥p是链表的工作指针
pre=head; ∥pre指向链表中数据域最小值结点的前驱
q=p; ∥q指向数据域最小值结点,初始假定是第一结点
while (p->next)
{if(p->next->datadata){pre=p;q=p->next;} ∥找到新的最小值结点
p=p->next;
}
if(q!=head->next) ∥若最小值是第一元素结点,则不需再操作
{pre->next=q->next; ∥将最小值结点从链表上摘下
q->next=head->next; ∥将q结点插到链表最前面
head->next=q;
}
}∥Delinsert
2.18 设la是带头结点的非循环双向链表的指针,其结点中除有prior,data和next外,还有一访问频度域freq,其值在链表初始使用时为0。当在链表中进行ListLocate(la,x)运算时,若查找失败,则在表尾插入值为x的结点;若查找成功,值为x的结点的freq值增1,并要求链表按freq域值非增(递减)的顺序排列,且最近访问的结点排在频度相同的结点的后面,使频繁访问的结点总是靠近表头。试编写符合上述要求的ListLocate(la,x)运算的算法,返回找到结点的指针。
【题目分析】首先在双向链表中查找数据值为x的结点,查到后,将结点从链表上摘下,然后再顺结点的前驱链查找该结点的位置。
DLinkList ListLocate(DLinkedList L,ElemType x)
∥ L是带头结点的按访问频度递减的双向链表,本算法先查找数据x
∥查找成功时结点的访问频度域增1,最后将该结点按频度递减插入链表中
{DLinkList p=L->next,q; ∥p为L表的工作指针,q为p的前驱,用于查找插入位置
while(p && p->data!=x) p=p->next;∥ 查找值为x的结点
if(!p) {printf(“不存在所查结点\n”); exit(0);}
else { p->freq++; ∥ 令元素值为x的结点的freq域加1
p->next->prior=p->prior; ∥ 将p结点从链表上摘下
p->prior->next=p->next;
q=p->prior; ∥ 以下查找p结点的插入位置
while(q !=L && q->freqfreq) q=q->prior;
p->next=q->next; q->next->prior=p;∥ 将p结点插入
p->prior=q; q->next=p;
}
return(p); ∥返回值为x的结点的指针
} ∥ 算法结束
2.19 三个带头结点的线性链表la、lb和lc中的结点均依元素值自小至大非递减排列(可能存在两个以上值相同的结点),编写算法对la表进行如下操作:使操作后的la中仅留下三个表中均包含的数据元素的结点,且没有值相同的结点,并释放所有无用结点。限定算法的时间复杂度为O(m+n+p),其中m、n和p分别为三个表的长度。
【题目分析】 留下三个链表中公共数据,首先查找两表la和B中公共数据,再去lc中找有无该数据。要消除重复元素,应记住前驱,要求时间复杂度O(m+n+p),在查找每个链表时,指针不能回溯。
LinkedList lcommon(LinkedList la,lb,lc)
∥本算法使la表留下la、lb和lc三个非递减有序表共同结点,无重复元素
{pa=la->next;pb=lb->next;pc=lc->next;
∥pa,pb和pc分别是la,lb和lc三个表的工作指针
pre=la;
la->data=MaxElemType ∥pre是la表当前结点的前驱结点的指针,头结点作监视哨
while(pa && pb && pc) ∥当la,lb和lc表均不空时,查找三表共同元素
{while(pa&&pa->data==pre->data)
{u=pa; pa=pa->next; free(u);}//删la中相同元素
while(pb && pc)
if(pb->datadata)pb=pb->next; ∥结点元素值小时,后移指针
else if(pb->data>pc->data)pc=pc->next;
else break ;∥处理lb和lc表元素值相等的结点
if(pb && pc)
{while(pa && pa->datadata){u=pa;pa=pa->next;free(u);}
if(pa && pa->data>pc->data){pb=pb->next; pc=pc->next;}
else if(pa && pa->data==pc->data)∥pc,pa和pb对应结点元素值相等
{pre->next=pa;pre=pa;pa=pa->next;∥将新结点链入la表
pb=pb->next;pc=pc->next; ∥链表的工作指针后移
}∥pc,pa和pb对应结点元素值相等
} }∥while(pa && pb && pc)
pre->next=null; ∥置新la表表尾
while(pa!=null) ∥删除原la表剩余元素。
{u=pa;pa=pa->next;free(u);}
}∥算法结束
【算法讨论】 算法中la表、lb表和lc表均从头到尾(严格说lb、lc中最多一个到尾)遍历一遍,算法时间复杂度符合O(m+n+p)。算法主要由while(pa && pb && pc)控制。三表有一个到尾则结束循环。要注意头结点的监视哨的作用,否则第一个结点要特殊处理。算法最后要给新la表置结尾标记,同时若原la表没到尾,还应释放剩余结点所占的存储空间。
第3章 栈和队列
一、基础知识题
3.1 有五个数依次进栈:1,2,3,4,5。在各种出栈的序列中,以3,4先出的序列有哪几个。(3在4之前出栈)。
【解答】34215 ,34251, 34521
3.2 铁路进行列车调度时,常把站台设计成栈式结构,若进站的六辆列车顺序为:1,2,3,4,5,6, 那么是否能够得到435612, 325641, 154623和135426的出站序列,如果不能,说明为什么不能; 如果能, 说明如何得到(即写出"进栈"或"出栈"的序列)。
【解答】输入序列为123456,不能得出435612和154623。不能得到435612的理由是,输出序列最后两元素是12,前面4个元素(4356)得到后,栈中元素剩12,且2在栈顶,不可能让栈底元素1在栈顶元素2之前出栈。不能得到154623的理由类似,当栈中元素只剩23,且3在栈顶,2不可能先于3出栈。
得到325641的过程如下:1 2 3顺序入栈,32出栈,得到部分输出序列32;然后45入栈,5出栈,部分输出序列变为325;接着6入栈并退栈,部分输出序列变为3256;最后41退栈,得最终结果325641。
得到135426的过程如下:1入栈并出栈,得到部分输出序列1;然后2和3入栈,3出栈,部分输出序列变为13;接着4和5入栈,5,4和2依次出栈,部分输出序列变为13542;最后6入栈并退栈,得最终结果135426。
3.3 若用一个大小为6的数组来实现循环队列,且当前rear和front的值分别为0和3,当从队列中删除一个元素,再加入两个元素后,rear和front的值分别为多少?
【解答】2和 4
3.4 设栈S和队列Q的初始状态为空,元素e1,e2,e3,e4,e5和e6依次通过栈S,一个元素出栈后即进队列Q,若6个元素出队的序列是e3,e5,e4,e6,e2,e1,则栈S的容量至少应该是多少?
【解答】 4
3.5 循环队列的优点是什么,如何判断“空”和“满”。
【解答】循环队列解决了常规用0--m-1的数组表示队列时出现的“假溢出”(即队列未满但不能入队)。在循环队列中我们仍用队头指针等于队尾指针表示队空,而用牺牲一个单元的办法表示队满,即当队尾指针加1(求模)等于队头指针时,表示队列满。也有通过设标记以及用一个队头或队尾指针加上队中元素个数来区分队列的“空”和“满”的。
3.6 设长度为n的链队列用单循环链表表示,若只设头指针,则入队和出队的时间如何?若只设尾指针呢?
【解答】若只设头指针,则入队的时间为O(n),出队的时间为O(1)。若只设尾指针,则入队和出队的时间均为O(1)。
3.7 指出下面程序段的功能是什么?
(1) void demo1(SeqStack S)
{int i,arr[64],n=0;
while(!StackEmpty(S)) arr[n++]=Pop(S);
for(i=0;i1) printf(i--);
}
【解答】void digui(int n)
{if(n>1){printf(n);
digui(n-1);
}
}
3.9 写出下列中缀表达式的后缀表达式:
(1)A*B*C (2)(A+B)*C-D (3)A*B+C/(D-E) (4)(A+B)*D+E/(F+A*D)+C
【解答】(1)ABC**
(2)AB+C*D-
(3)AB*CDE-/+
(4)AB+D*EFAD*+/+C+
3.10 选择题:循环队列存储在数组A[0..m]中,则入队时的操作为( )。
A. rear=rear+1 B. rear=(rear+1) % (m-1)
C. rear=(rear+1) % m D. rear=(rear+1) % (m+1)
【解答】D
3.11 选择题:4个园盘的Hahoi塔,总的移动次数为( )。
A.7 B. 8 C.15 D.16
【解答】C
3.12选择题:允许对队列进行的操作有( )。
A. 对队列中的元素排序 B. 取出最近进队的元素
C. 在队头元素之前插入元素 D. 删除队头元素
【解答】D
二、算法设计题
3.13 利用栈的基本操作,编写求栈中元素个数的算法。
【题目分析】 将栈值元素出栈,出栈时计数,直至栈空。
【算法】 int StackLength(Stack S)
{//求栈中元素个数
int n=0;
while(!StackEmpty(S)
{n++; Pop(S);
}
return n;
}
算法讨论:若要求统计完元素个数后,不能破坏原来栈,则在计数时,将原栈导入另一临时栈,计数完毕,再将临时栈倒入原栈中。
int StackLength(Stack S)
{//求栈中元素个数
int n=0;
Stack T;
StackInit(T); //初始化临时栈T
while(!StackEmpty(S)
{n++; Push(T,Pop(S));
}
while(!StackEmpty(T)
{Push(S,Pop(T));
}
return n;
}
3.14 双向栈S是在一个数组空间V[m]内实现的两个栈,栈底分别处于数组空间的两端。试为此双向栈设计栈初始化Init(S)、入栈Push(S,i,x)、出栈Pop(S,i)算法,其中i为0或1,用以指示栈号。
[题目分析]两栈共享向量空间,将两栈栈底设在向量两端,初始时,s1栈顶指针为-1,s2栈顶为m。两栈顶指针相邻时为栈满。两栈顶相向、迎面增长,栈顶指针指向栈顶元素。
#define ElemType int ∥假设元素类型为整型
typedef struct
{ElemType V[m]; ∥栈空间
int top[2]; ∥top为两个栈顶指针
}stk;
stk S; ∥S是如上定义的结构类型变量,为全局变量
(1) 栈初始化
int Init()
{S.top[0]=-1;
S.top[1]=m;
return 1; //初始化成功
}
(2) 入栈操作:
int push(stk S ,int i,int x)
∥i为栈号,i=0表示左栈,i=1为右栈,x是入栈元素。入栈成功返回1,否则返回0
{if(i<0||i>1){printf(“栈号输入不对\n”);exit(0);}
if(S.top[1]-S.top[0]==1) {printf(“栈已满\n”);return(0);}
switch(i)
{case 0: S.V[++S.top[0]]=x; return(1); break;
case 1: S.V[--S.top[1]]=x; return(1);
}
}∥push
(3) 退栈操作
ElemType pop(stk S,int i)
∥退栈。i代表栈号,i=0时为左栈,i=1时为右栈。退栈成功返回退栈元素
∥否则返回-1
{if(i<0 || i>1){printf(“栈号输入错误\n”);exit(0);}
switch(i)
{case 0: if(S.top[0]==-1) {printf(“栈空\n”);return(-1);}
else return(S.V[S.top[0]--]);
case 1: if(S.top[1]==m {printf(“栈空\n”); return(-1);}
else return(S.V[S.top[1]++]);
}∥switch }∥算法结束
(4) 判断栈空
int Empty();
{return (S.top[0]==-1 && S.top[1]==m);
}
[算法讨论] 请注意算法中两栈入栈和退栈时的栈顶指针的计算。s1(左栈)是通常意义下的栈,而s2(右栈)入栈操作时,其栈顶指针左移(减1),退栈时,栈顶指针右移(加1)。
3.15设以数组Q[m]存放循环队列中的元素,同时设置一个标志tag,以tag=0和tag=1来区别在队头指针(front)和队尾指针(rear)相等时,队列状态为“空”还是“不空”。试编写相应的入队(QueueIn)和出队(QueueOut)算法。
(1) 初始化
SeQueue QueueInit(SeQueue Q)
{//初始化队列
Q.front=Q.rear=0; Q.tag=0;
return Q;
}
(2) 入队
SeQueue QueueIn(SeQueue Q,int e)
{//入队列
if((Q.tag==1) && (Q.rear==Q.front)) printf("队列已满\n");
else {Q.rear=(Q.rear+1) % m;
Q.data[Q.rear]=e;
if(Q.tag==0) Q.tag=1; //队列已不空
}
return Q;
}
(3)出队
ElemType QueueOut(SeQueue Q)
{//出队列
if(Q.tag==0) printf("队列为空\n");
else
{Q.front=(Q.front+1) % m;
e=Q.data[Q.front];
if(Q.front==Q.rear) Q.tag=0; //空队列
}
return(e);
}
3.16假设用变量rear和length分别指示循环队列中队尾元素的位置和内含元素的个数。试给出此循环队列的定义,并写出相应的入队(QueueIn)和出队(QueueOut)算法。
【算法设计】
(1)循环队列的定义
typedef struct
{ElemType Q[m]; ∥ 循环队列占m个存储单元
int rear,length; ∥ rear指向队尾元素,length为元素个数
}SeQueue;
(2) 初始化
SeQueue QueueInit (SeQueue cq)
∥cq为循环队列,本算法进行队列初始化
{ cq.rear=0; cq.length=0; return cq;
}
(3) 入队
SeQueue QueueIn(SeQueue cq,ElemType x)
∥cq是以如上定义的循环队列,本算法将元素x入队
{if(cq.length==m) return(0); ∥ 队满
else {cq.rear=(cq.rear+1) % m; ∥ 计算插入元素位置
cq.Q[cq.rear]=x; ∥ 将元素x入队列
cq.length++; ∥ 修改队列长度
}
return (cq);
}
链接地址:https://www.zhuangpeitu.com/p-2885961.html