专升本数据结构试题解析

更新时间:2023-08-09 13:56:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

第2部分 习题解析

第1章 绪论

1.1 选择题

1. 算法的时间复杂度取决于( C )

A)问题的规模 B) 待处理数据的初态 C) A和B

【答案】C

2.计算机算法指的是解决问题的步骤序列,它必须具备(B ) 这三个特性。

A)可执行性、可移植性、可扩充性 B) 可执行性、确定性、有穷性

C) 确定性、有穷性、稳定性 D) 易读性、稳定性、安全性

【答案】B

5.从逻辑上可以把数据结构分为( C )两大类。

A)动态结构、静态结构 B)顺序结构、链式结构

C)线性结构、非线性结构 D)初等结构、构造型结构

【答案】C

6.在下面的程序段中,对x的赋值的语句频度为( C )

for(i=0;i<n;i++)

for(j=0;j<n;j++) x=x+1;

A) O(2n) B)O(n) C.O(n2) D.O(log2n)

【答案】C

7.下面的程序段中, n为正整数,则最后一行的语句频度在最坏情况下是(D )

for(i=n-1;i>=1;i--)

for(j=1;j<=i;j++)

if (A[j]>A[j+1])

A[j]与A[j+1]对换;

A. O(n) B) O(nlog2n) C) O(n3) D) O(n2)

【答案】D

1.2 填空题

2. 对于给定的n个元素,可以构造出的逻辑结构有_____________,_____________, _____________四种。

【答案】(1)集合 (2)线性结构 (3)树形结构(4)图状结构或网状结构

4.数据结构中评价算法的两个重要指标是_____________。

【答案】算法的时间复杂度和空间复杂度。

5. 数据结构是研讨数据的_____________和构定义相应的_____________,设计出相应的_____________。

【答案】(1)逻辑结构(2)物理结构(3)操作(运算)(4)算法。

6.一个算法具有5个特性:_____________、_____________、_____________,有零个或多个输入、有一个或多个输出。

【答案】(1)有穷性 (2)确定性 (3)可行性。

9.已知如下程序段

for(i=n;i>0;i--) {语句1}

{ x=x+1; {语句2}

for(j=n;j>=i;j--) {语句3}

y=y+1; {语句4}

}

语句1执行的频度为_____________;语句2执行的频度为_____________;语句3执行的频度为_____________;语句4执行的频度为_____________。

【答案】(1)n+1 (2)n (3)n(n+3)/2 (4)n(n+1)/2

10.在下面的程序段中,对x的赋值语句的频度为_____________(表示为n的函数)

for(i=0;i>n;i++)

for(j=0;j>i;j++)

for(k=0;k>j;k++)

x=x+delta;

数据结构上机实验与习题解析 亱店↘打烊oO

【答案】1+(1+2)+(1+2+3)+…+(1+2+…+n)=n(n+1)(n+2)/6 , O(n3)

11.下面程序段中带下划线的语句的执行次数的数量级是_____________。

i=1; while(i<n) i=i*2;

【答案】log2n

12. 计算机执行下面的语句时,语句s的执行次数为_____________。

for(i=l;i<n-l;i++)

for(j=n;j>=i;j--) s;

【答案】(n+3)(n-2)/2

13. 下面程序段的时间复杂度为_____________。(n>1)

sum=1;

for (i=0;sum<n;i++) sum+=1;

【答案】O(n)

第2章 线性表

2.1 选择题

1.对于线性表最常用的操作是查找指定序号的元素和在末尾插入元素,则选择( )最节省时间

A)顺序表 B)带头结点的双循环链表

C)单链表 D)带尾结点的单循环链表

【答案】A

2.若长度为n的线性表采用顺序存储结构,在其第i个位置插入一个新元素的算法时间复杂度为( )(1≤i≤n+1)。

A) O(0) B) O(1) C) O(n) D) O(n2)

【答案】C

3.双向链表中有两个指针域,prior和next,分别指向前驱及后继,设p指向链表中的一个结点,q指向一待插入结点,现要求在p前插入q,则正确的插入为( )

A) p->prior=q; q->next=p; p->prior->next=q; q->prior=p->prior;

B) q->prior=p->prior; p->prior->next=q; q->next=p; p->prior=q->next;

C) q->next=p; p->next=q; p->prior->next=q; q->next=p;

D) p->prior->next=q; q->next=p; q->prior=p->prior; p->prior=q;

【答案】D

4.在一个具有n个结点的有序单链表中插入一个新结点并仍然保持有序的时间复杂度是( )

A)O(nlog2n) B) O(1) C) O(n) D) O(n2)

【答案】C

5. 在一个以 h 为头指针的单循环链中,p 指针指向链尾结点的条件是( )

A)p->next==NULL B) p->next==h

C)p->next->next==h D) p->data==-1

【答案】B

6.对于一个具有n个结点的线性表,建立其单链表的时间复杂度是( )

A)O(n) B) O(1) C)O(nlog2n) D) O(n2)

【答案】A

8.在双向链表存储结构中,删除p所指的结点时须修改指针( )

A)p->prior->next=p->next p->next->prior=p->prior;

B)p->prior=p->prior->prior p->prior->next=p;

C)p->next->prior=p p->next=p->next->next

D)p->next=p->prior->prior p->prior=p->next->next;

【答案】A

9.线性表采用链式存储时,其元素地址( )

A)必须是连续的 B)一定是不连续的

C)部分地址是连续的 D)连续与否均可

【答案】D

2.2 填空题

1.线性表L=(a1,a2,…,an)用数组表示,假定删除表中任一元素的概率相同,则删除一个元素

第2部分 习题解析 亱店↘打烊oO

平均需要移动元素的个数是_____________。

【答案】(n-1)/2

2.在单链表中设置头结点的作用是_____________。

【答案】主要是使插入和删除等操作统一,在第一个元素之前插入元素和删除第一个结点不必另作判断。另外,不论链表是否为空,链表头指针不变。

3.线性表的顺序存储是通过_____________来反应元素之间的逻辑关系,而链式存储结构是通过_____________来反应元素之间的逻辑关系。

【答案】(1)数据元素的前后顺序 (2)元素中的指针

4.当对一个线性表经常进行的是存取操作,而很少进行插入和删除操作时,则采用_____________存储结构最节省时间,相反当经常进行插入和删除操作时,则采用_____________存储结构最节省时间。

【答案】(1)顺序 (2)链式

5.对于一个具有n个结点的单链表,在已知的结点*p后插入一个新结点的时间复杂度为_____________,在给定值为x的结点后插入一个新结点的时间复杂度为_____________。

【答案】(1)O(1) (2)O(n)

7. 对于双向链表,在两个结点之间插入一个新结点需修改的指针共_____________个,单链表为_____________个。

【答案】(1)4 (2)2

8. 循环单链表的最大优点是_____________。

【答案】从任一结点出发都可访问到链表中每一个元素。

9.若要在一个不带头结点的单链表的首结点*p结点之前插入一个*s结点时,可执行下列操作: p->next=s;

t=p->data; 【答案】(1)p->next (2)s->data (3) t

10.某线性表采用顺序存储结构,每个元素占据4个存储单元,首地址为100,则下标为11的(第12个)元素的存储地址为_____________。

【答案】144

11.带头结点的双循环链表L中只有一个元素结点的条件是_____________。

【答案】L->next->next==L

2.3 判断题

1.取线性表的第i个元素的时间同i的大小有关( )

【答案】×

2.线性表的特点是每个元素都有一个前驱和一个后继( )

【答案】×

3. 顺序存储方式的优点是存储密度大,且插入、删除运算效率高( )

【答案】×

4.线性表采用链表存储时,结点的存储空间可以是不连续的( )

【答案】√

5.链表是采用链式存储结构的线性表,进行插入、删除操作时,在链表中比在顺序存储结构中效率高( )

【答案】√

6.顺序存储方式只能用于存储线性结构( )

【答案】×

【解析】线性结构、树型结构和图状结构均可用顺序存储表示。

9.顺序存储结构的主要缺点是不利于插入或删除操作( )

【答案】√

10.顺序存储方式插入和删除时效率太低,因此它不如链式存储方式好( )

【答案】×

2.4 程序设计题

1.设顺序表va中的数据元素递增有序。试设计一个算法,将x插入到顺序表的适当位置上,以保持

数据结构上机实验与习题解析 亱店↘打烊oO

该表的有序性。

【算法源代码】

void Insert_SqList(SqList va,int x)/*把x插入递增有序表va中*/

{ int i;

if(va.length> MAXSIZE) return;

for(i=va.length-1;va.elem[i]>x&&i>=0;i--)

va.elem[i+1]=va.elem[i];

va.elem[i+1]=x;

va.length++;

}/*Insert_SqList*/

2.设 A=(a1,a2,…,am) 和 B=(b1,b2,…,bn)均为顺序表,试设计一个比较A,B大小的算法(请注意:在算法中,不要破坏原表A和B)。

【算法分析】比较顺序表A和B,并用返回值表示结果,值为1,表示A>B;值为-1,表示A<B;值为0,表示A=B。

1)当两个顺序表可以互相比较时,若对应元素不等,则返回值为1或-1;

2)当两个顺序表可以互相比较的部分完全相同时,若表长也相同,则返回值为0;否则,哪个较长,哪个就较大

【算法源代码】

int ListComp(SqList A,SqList B)

{

for(i=1;i<=A.length&&i<=B.length;i++)

if(A.elem[i]!=B.elem[i])

return A.elem[i]>B.elem[i]?1:-1;

if(A.length==B.length) return 0;

return A.length>B.length?1:-1;

/*当两个顺序表可以互相比较的部分完全相同时,哪个较长,哪个就较大*/

}/*ListComp */

3.已知指针 ha和 hb分别指向两个单链表的头结点,并且已知两个链表的长度分别为m和n。试设计一个算法将这两个链表连接在一起(即令其中一个表的首元结点连在另一个表的最后一个结点之后),假设指针hc指向连接后的链表的头结点,并要求算法以尽可能短的时间完成连接运算。

【算法分析】

1)单链表ha的头结点作为连接后的链表的头结点,即hc=ha;

2)查找单链表ha的最后一个结点,由指针p指向,即p->next==NULL;

3)将单链表hb的首元结点(非头结点)连接在p之后,即p->next=hb->next;

4)回收单链表hb的头结点空间

【算法源代码】

void ListConcat(LinkList ha,LinkList hb,LinkList *hc)

/*把链表hb接在ha后面形成链表hc*/

{

*hc=ha;

p=ha;/*由指针p指向ha的尾元结点*/

p=p->next;

p->next=hb->next;

free(hb);

}/*ListConcat */

4.试设计一个算法,在无头结点的动态单链表上实现线性表操作INSERT(L,i,b),并和在带头结点的动态单链表上实现相同操作的算法进行比较。

【算法分析】

1)生成新结点存放元素b,由指针new指向;

2)将new插入在单链表的第i个元素的位置上:若i==1,new插在链表首部;否则查找第i-1个结点,由指针p指向,然后将new插在p之后。

【算法源代码】

void Insert(LinkList *L,int i,int b)

{ LinkList new;

第2部分 习题解析 亱店↘打烊oO

new=(LinkList*)malloc(sizeof(LNode));

new->data=b;

if(i==1)

{/*插入在链表头部*/

New->next=*L;

*L=new;

}

else

{ /*插入在第i个元素的位置*/

p=*L;

while(--i>1) p=p->next;

new->next=p->next;p->next=new;

}

}/*Insert */

5.已知线性表中的元素以值递增有序排列,并以单链表作存储结构。试设计一个高效的算法,删除表中所有值大于 mink且小于 maxk的元素(若表中存在这样的元素),同时释放被删结点空间(注意:mink和maxk是给定的两个参变量。它们的值可以和表中的元素相同,也可以不同)。

【算法分析】

1)查找最后一个不大于mink的元素结点,由指针p指向;

2)如果还有比mink更大的元素,查找第一个不小于maxk的元素,由指针q指向;

3)p->next=q,即删除表中所有值大于 mink且小于 maxk的元素。

【算法源代码】

void Delete_Between(LinkList *L,int mink,int maxk)

{

p=*L;

while(p->next->data<=mink) p=p->next; /*p是最后一个不大于mink的元素*/

if(p->next) /*如果还有比mink更大的元素*/

{

q=p->next;

while(q->data<maxk) q=q->next; /*q是第一个不小于maxk的元素*/

p->next=q;

}

}/*Delete_Between */

6.已知线性表中的元素以值递增有序排列,并以单链表作存储结构。试设计一个高效的算法,删除表中所有值相同的多余元素(使得操作后的线性表中所有元素的值均不相同),同时释放被删结点空间。

【算法分析】

1)初始化指针p和q,分别指向链表中相邻的两个元素;

2)当p->next不为空时,做如下处理:

①若相邻两元素不相等时,p和q都向后推一步;

②否则,当相邻元素相等时,删除多余元素。

【算法源代码】

void Delete_Equal(LinkList *L)

{

p=(*L)->next;q=p->next; /*p和q指向相邻的两个元素*/

while(p->next)

{

if(p->data!=q->data) /*若相邻两元素不相等时,p和q都向后推一步*/

{

p=p->next;

q=p->next;

}

else

{

while(q->data==p->data) /*当相邻元素相等时删除多余元素*/

{

数据结构上机实验与习题解析 亱店↘打烊oO

r=q;

q=q->next;

free(r);

}

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

}/*else*/

}/*while*/

}/*Delete_Equal */

7.试设计一个算法,对带头结点的单链表实现就地逆置。

【算法分析】

1)空表或长度为1的表,不做任何处理;

2)表长大于2时,做如下处理:

①首先将整个链表一分为二,即从链表的第一元素结点处断开;

②逐个地把剩余链表的当前元素q插入到链表的头部。

【算法源代码】

void LinkList_reverse(LinkList L)

{ if(!L->next||!L->next->next) return;

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

p->next=NULL; /*从链表的第一元素结点处断开*/

while(s->next)

{q->next=p;p=q;

q=s;s=s->next; /*把L的元素逐个插入新表表头*/

}

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

}/*LinkList_reverse*/

8.设线性表A=(a1,a2,…,am) 和 B=(b1,b2,…,bn),试设计一个按下列规则合并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均未显式存储。

【算法分析】

1)初始化指针p指向链表A的当前元素,指针q指向链表B的当前元素;

2)当链表A和B均为结束时,做如下处理:

①将B的元素插入

②若A非空,将A的元素插入

③指针p和q同时后移

【算法源代码】

void merge1(LinkList A,LinkList B,LinkList *C)

{p=A->next;q=B->next;*C=A;

while(p&&q)

{ s=p->next;p->next=q; /*将B的元素插入*/

if(s)

{ t=q->next;

q->next=s; /*若A非空,将A的元素插入*/

}

p=s;q=t; /*指针p和q同时后移*/

}/*while*/

}/*merge1 */

9.假设有两个按元素值递增有序排列的线性表A和B,均以单链表作存储结构,请设计一个算法将A表和B表归并成一个按元素值递减有序(即非递增有序,允许表中含有值相同的元素)排列的线性表C,并要求利用原表(即 A表和B表)的结点空间构造C表。

【算法分析】按从小到大的顺序依次把A和B的元素插入新表的头部pc处,最后处理A或B的剩余

第2部分 习题解析 亱店↘打烊oO

元素。

【算法源代码】

void reverse_merge(LinkList A,LinkList B,LinkList *C)

{ LinkList pa,pb,pre;

pa=A->next;pb=B->next; /*pa和pb分别指向A和B的当前元素*/

pre=NULL;

while(pa||pb)

{ if(pa->data<pb->data||!pb) /*将A的元素插入新表*/

{ pc=pa;q=pa->next;pa->next=pre;pa=q; }

else /*将B的元素插入新表*/

{ pc=pb;q=pb->next;pb->next=pre;pb=q; }

pre=pc;

}

*C=A;

A->next=pc; /*构造新表头*/

}/*reverse_merge*/

10.已知A,B和C为三个递增有序的线性表,现要求对A表作如下操作:删去那些既在B表中出现又在C表中出现的元素。试对顺序表编写实现上述操作的算法,并分析你的算法的时间复杂度(注意:题中没有特别指明同一表中的元素值各不相同)。

【算法分析】先从B和C中找出共有元素,记为same,再在A中从当前位置开始, 凡小于same的元素均保留(存到新的位置),等于same的就跳过,到大于same时就再找下一个same。

【算法源代码】

void SqList_Intersect_Delete(SqList *A,SqList B,SqList C)

{i=0;j=0;k=0;m=0; /*i指示A中元素原来的位置,m为移动后的位置*/

while(i<(*A).length&&j<B.length&& k<C.length)

{ if(B.elem[j]<C.elem[k]) j++;

else if(B.elem[j]>C.elem[k]) k++;

else{

same=B.elem[j]; /*找到了相同元素same*/

while(B.elem[j]==same) j++;

while(C.elem[k]==same) k++; /*j和k后移到新的元素*/

while(i<(*A).length&&(*A).elem[i]<same)

(*A).elem[m++]=(*A).elem[i++]; /*需保留的元素移动到新位置*/

while(i<(*A).length&&(*A).elem[i]==same) i++; /*跳过相同的元素*/

}

}/*while*/

while(i<(*A).length)

(*A).elem[m++]=(*A).elem[i++]; /*A的剩余元素重新存储*/

(*A).length=m;

}/* SqList_Intersect_Delete*/

11.设L为单链表的头结点地址,其数据结点的数据都是正整数且无相同的,试设计利用直接插入的原则把该链表整理成数据递增的有序单链表的算法。

【算法分析】本题明确指出单链表带头结点,其结点数据是正整数且不相同,要求利用直接插入原则把链表整理成递增有序链表。这就要求从第二结点开始,将各结点依次插入到有序链表中。

【算法源代码】

void InsertSort (LinkList la)

{if(la->next!=NULL) /*链表不为空表*/

{p=la->next->next; /*p指向第一结点的后继*/

la->next->next=NULL;

/*直接插入原则认为第一元素有序,然后从第二元素起依次插入*/

while(p!=NULL)

{r=p->next;/*暂存p的后继*/

q=la;

while(q->next!=NULL&&q->next->data<p->data)q=q->next;/*查找插入位置*/

数据结构上机实验与习题解析 亱店↘打烊oO

p->next=q->next;/*将p结点链入链表*/

q->next=p;

p=r;

}

12.设有一个双向循环链表,每个结点中除有 prior,data和 next三个域外,还增设了一个访问频度域freq。在链表被起用之前,频度域freq的值均初始化为零,而每当对链表进行一次LOCATE(L,X)的操作后,被访问的结点(元素值等于X的结点)中的频度域freq的值便增1,同时调整链表中结点之间的次序,使其按访问频度非递增的次序顺序排列,以便始终保持被频繁访问的结点总是靠近表头结点。试编写符合上述要求的 LOCATE操作的算法。

【算法分析】

1)在双向链表中查找数据值为x的结点,由指针p指向,若找不到,直接返回,否则执行第2步;

2)修改x结点的访问频度freq,并将结点从链表上摘下;

3)顺结点的前驱链查找该结点的位置,即找到一个结点的访问频度大于x结点的访问频度,由指针q指向;若q和p不是相邻结点,调整位置,把p插在q之后。

【算法源代码】

DuLNode * Locate_DuList(DuLinkList *L,int x)

{ p=(*L)->next;

while(p.data!=x&&p!= (*L)) p=p->next;

if(p==(*L)) return NULL; /*没找到x结点*/

p->freq++;

p->pre->next=p->next;p->next->pre=p->pre; /*将x结点从链表上摘下*/

q=p->pre;

while(q->freq<=p->freq&&p!= (*L)) q=q->pre; /*查找插入位置*/

if(q!=p->pre) /*将x结点插入*/

{q->next->pre=p;p->next=q->next;

q->next=p;p->pre=q; /*调整位置*/

}

return p;

}/*Locate_DuList */

13.已知三个带头结点的线性链表A、B和C中的结点均依元素值自小至大非递减排列(可能存在两个以上值相同的结点),编写算法对A表进行如下操作:使操作后的链表A中仅留下三个表中均包含的数据元素的结点,且没有值相同的结点,并释放所有无用结点。限定算法的时间复杂度为O(m+n+p),其中m、n和p分别为三个表的长度。

【算法分析】留下三个链表中公共数据,首先查找两表A和B中公共数据,再去C中找有无该数据。要消除重复元素,应记住前驱,要求时间复杂度O(m+n+p),在查找每个链表时,指针不能回溯。

【算法源代码】

LinkList Common(LinkList A, LinkList B, LinkList C)

{pa=A->next;pb=B->next; pc=C->next; /*pa,pb和pc是工作指针*/

pre=A;

while(pa && pb && pc) /*当三表均不空时,查找共同元素*/

{ while(pa && pb)

if(pa->data<pb->data) /*处理pa结点,后移指针*/

{u=pa;pa=pa->next;free(u);}

else if(pa->data> pb->data)pb=pb->next;

else if (pa && pb) /*处理A和B表元素值相等的结点*/

{while(pc && pc->data<pa->data)pc=pc->next;

if(pc)

{if(pc->data>pa->data) /*处理pa结点,后移指针*/

{u=pa;pa=pa->next;free(u);}

else

{if(pre==A) /*结果表中第一个结点*/

{ pre->next=pa;pre=pa;pa=pa->next}

else if(pre->data==pa->data) /*重复结点不链入A表*/

{u=pa;pa=pa->next;free(u);}

第2部分 习题解析 亱店↘打烊oO

else

{pre->next=pa;pre=pa;pa=pa->next;}/*将新结点链入A表 */

pb=pb->next;pc=pc->next; /* 链表的工作指针后移*/

}

}

else

if(pa==NULL)pre->next=NULL; /*若A表已结束,置A表表尾*/

else /*处理原A表未到尾而B或C到尾的情况*/

{pre->next=NULL; /*置A表表尾标记*/

while(pa!=NULL) /*删除原A表剩余元素。*/

{u=pa;pa=pa->next;free(u);}

}

}

14.设 head为一单链表的头指针,单链表的每个结点由一个整数域data和指针域next组成,整数在单链表中是无序的。编一函数,将 head链中结点分成一个奇数链和一个偶数链,分别由p,q指向,每个链中的数据按由小到大排列。程序中不得使用malloc申请空间。

【算法分析】本题要求将一个链表分解成两个链表,两个链表都要有序,两链表建立过程中不得使用malloc申请空间,这就是要利用原链表空间,随着原链表的分解,新建链表随之排序。

【算法源代码】

discreat(LinkList p, LinkList q, LinkList head)

{ p=NULL; q=NULL;/*p和q链表初始化为空表*/

s=head;

while(s!=NULL)

{r=s->next; /*暂存s的后继*/

if(s->data%2==0) /*处理偶数*/

if (p==NULL) {p=s;p->next=NULL;} /*第一个偶数结点*/

else { pre=p;

if(pre->data>s->data)

{s->next=pre;p=s;}/*插入当前最小值结点*/

else

{while (pre->next!=NULL)

if (pre->next->data<s->data) pre=pre->next;/*查找插入位置*/

s->next=pre->next; /*链入结点*/

pre->next=s;}

}

else/*处理奇数链

if (q==NULL) {q=s;q->next=NULL;} /*第一奇数结点*/

else

{pre=q;

if (pre->data>s->data) {s->next=pre; q=s;} /*修改头指针*/

else

{while (pre->next!=NULL) /*查找插入位置*/

if (pre->next->data<s->data) pre=pre->next;

s->next=pre->next; /*链入结点*/

pre->next=s; }

}/*结束奇数链结点*/

s=r; /*s指向新的待排序结点*/

}

}

第3章 桟和队列

3.1 选择题

1.一个栈的输入序列为123 n,若输出序列的第一个元素是n,输出第i(1≤i≤n)个元素是( )

A)不确定 B)n-i+1 C)i D)n-i

【答案】B

【解析】根据栈的性质(LIFO),若输出的第一个元素是n,则表明所有的元素已经入栈,则出栈顺序为n,n-1, ,3,2,1。

2.设栈S和队列Q的初始状态为空,元素e1,e2,e3,e4,e5和e6依次通过栈S,一个元素出栈后即进队列Q,若6个元素出队的序列是e2,e4,e3,e6,e5,e1则栈S的容量至少应该是( )

A)6 B)4 C)3 D)2

【答案】C

【解析】根据栈的性质(LIFO)得,e2出栈前,栈中存有e1和e2两个元素,e4出栈前,栈中存有e1、e3和e4三个元素,e4和e3出栈以后,e5和e6入栈,栈中同样存在e1、e5和e6三个元素,然后三个元素依次出栈,所以栈的容量至少应该为3。

3.若一个栈以向量V[1..n]存储,初始栈顶指针top为n+1,则下面x进栈的正确操作是( )

A)top=top+1; V[top]=x B)V[top]=x; top=top+1

C)top=top-1; V[top]=x D)V[top]=x; top=top-1

【答案】C

【解析】栈式运算受限的线性表,只允许在栈顶进行插入和删除操作。本题中栈顶指针为n+1,该数组将栈顶放在了下标大的一端,所以在进行入栈操作时top指针应该进行减一操作。通常元素进栈的操作为:先移动栈顶指针后存入元素。

4.如果我们用数组A[1..100]来实现一个大小为100的栈,并且用变量top来指示栈顶,top的初值为0,表示栈空。请问在top为100时,再进行入栈操作,会产生( )

A)正常动作 B)溢出 C)下溢 D)同步

【答案】B

【解析】当top为100时,表示栈已经满了,此时再进行入栈操作,则会造成溢出。

5.栈在( )中应用。

A)递归调用 B)子程序调用 C)表达式求值 D)A,B,C

【答案】D

6.表达式3* 2^(4+2*2-6*3)-5求值过程中当扫描到6时,对象栈和算符栈为( ),其中^为乘幂 。

A)3,2,4,1,1;(*^(+*- B)3,2,8;(*^-

C)3,2,4,2,2;(*^(- D)3,2,8;(*^(-

【答案】D

【解析】根据表达式求值的基本思想,在扫描表达式时,依次读入表达式的每个字符,若是操作数则进对象栈,若为运算符则和运算符栈的栈顶运算符比较优先级后做相应的操作。

7.用链接方式存储的队列,在进行删除运算时( )

A)仅修改头指针 B)仅修改尾指针

C)头、尾指针都要修改 D)头、尾指针可能都要修改

【答案】D

【解析】若队列中的元素多于一个,删除队列中的队尾元素,只需修改队尾指针;若队列中只有一个元素,删除该元素后,队头队尾指针都需要修改。

8.循环队列A[0..m-1]存放其元素值,用front和rear分别表示队头和队尾,则当前队列中的元素数是( )

A)(rear-front+m)%m B)rear-front+1

C)rear-front-1 D)rear-front

【答案】A

【解析】循环队列是解决假溢出的问题,通常把一维数组看成首尾相接。在循环意义下的求元素个数的运算可以利用求模运算。

9.若用一个大小为6的数组来实现循环队列,且当前rear和front的值分别为0和3,当从队列中删除一个元素,再加入两个元素后,rear和front的值分别为多少?( )

数据结构上机实验与习题解析 亱店↘打烊oO

A)1和 5 B)2和4 C)4和2 D)5和1

【答案】B

【解析】循环队列是解决假溢出的问题,通常把一维数组看成首尾相接。在循环意义下的加1运算通常用求模运算来实现。所以入队和出队时的操作分别为:rear=(rear+1)%m,front=(front+1)%m。

10.栈和队列的共同点是( )

A)都是先进先出 B)都是先进后出

C)只允许在端点处插入和删除元素 D)没有共同点

【答案】C

【解析】栈和队列都是运算受限的线性表,只允许在表端点处进行操作。

11.在一个链队列中,假定front和rear分别为队头和队尾指针,则插入*s结点的操作为( )

A)front->next=s;front=s; B)s->next=rear;rear=s;

C)rear->next=s;rear=s; D)s->next=front;front=s;

【答案】C

【解析】队列是运算受限的线性表(FIFO),插入元素只能插在队尾,所以需修改队尾指针。

12.判定一个栈S(元素个数最多为MAXSIZE)为空和满的条件分别为( )

A)S->top!=-1 S->top!=MAXSIZE-1

B)S->top=-1 S->top=MAXSIZE-1

C)S->top=-1 S->top!=MAXSIZE-1

D)S->top!=-1 S->top=MAXSIZE-1

【答案】B

13.循环顺序队列中是否可以插入下一个元素( )

A)与队头指针和队尾指针的值有关

B)只与队尾指针的值有关,与队头指针的值无关

C)只与数组大小有关,而与队头指针和队尾指针的值无关

D)与曾经进行过多少次插入操作有关

【答案】A

【解析】在循环队列中判断队满的条件为:(q.rear+1)%m==q.front是否为真,从中可以看出,与队头指针和队尾指针的值有关。

14.最不适合用作链队的链表是( )

A)只带队头指针的非循环双链表 B)只带队头指针的循环双链表

C)只带队尾指针的非循环双链表 D)只带队尾指针的循环单链表

【答案】A

【解析】链队是在链表的两端进行操作,而在A中查找链表最后一个结点的时间复杂度为O(n)。

15.下列哪中数据结构常用于系统程序的作业调度( )

A)栈 B)队列 C)链表 D)数组

【答案】B

【解析】作业调度采用先到先服务的方式,因此最适合的数据结构为队列。

3.2 填空题

1.栈是_____________的线性表,其运算遵循_____________的原则。

【答案】(1)操作受限(或限定仅在表尾进行插入和删除操作) (2)后进先出

2.设有一个空栈,栈顶指针为1000H(十六进制),现有输入序列为1,2,3,4,5,经过PUSH,PUSH,POP,PUSH,POP,PUSH,PUSH之后,输出序列是_____________,而栈顶指针值是_____________H。设栈为顺序栈,每个元素占4个字节。

【答案】(1)23 (2)100CH

【解析】PUSH为入栈操作,POP为出栈操作。根据栈的性质,经过PUSH,PUSH,POP运算之后,栈中存在元素1,输出数据为2,然后经过PUSH,POP,3入栈,3出栈,然后经过PUSH,PUSH之后4,5入栈,此时出栈序列为2,3,栈中元素为1,4,5;每个元素占4个字节,所以栈顶指针的值为1000H+3*4=100CH(十六进制数)

3.循环队列的引入,目的是为了克服_____________。

【答案】假溢出时大量移动数据元素。

4.队列是限制插入只能在表的一端,而删除在表的另一端进行的线性表,其特点是_____________。

第2部分 习题解析 亱店↘打烊oO

【答案】先进先出

5.已知链队列的头尾指针分别是f和r,则将值x入队的操作序列是_____________。

【答案】

s=(LinkList)malloc(sizeof(LNode));

s->data=x;

s->next=r->next;

r->next=s;

r=s;

【解析】根据队列的性质,新插入的元素永远插在队尾。

6.区分循环队列的满与空,只有两种方法,它们是_____________和_____________。

【答案】(1)牺牲一个存储单元 (2)设标记

7.表达式23+((12*3-2)/4+34*5/7)+108/9的后缀表达式是_____________。

【答案】23.12.3*2-4/34.5*7/++108.9/+(注:表达式中的点(.)表示将数隔开,如23.12.3是三个数)

【解析】表达式的后缀表达式是将表达式中的运算符写在两个操作数之后。转换原则如下:

(1)从左到右扫描表达式,若读到的是操作数,则直接把它输出

(2)若读到的是运算符:

①该运算符为左括号“(”,则直接入栈

②该运算符为右括号“)”,则输出栈中运算符,直到遇到左括号为止

③该运算符为非括号运算符,则与栈顶元素做优先级比较:若比栈顶元素的优先级高或相等,则直接入栈;若比栈顶元素的优先级低,则输出栈顶元素。

(3)当表达式扫描完后栈中还有运算符,则依次输出运算符,直到栈空。

8.用下标0开始的N元数组实现循环队列时,为实现下标变量M加1后在数组有效下标范围内循环,可采用的表达式是:M=_____________。

【答案】(M+1)% N;

【解析】循环队列是解决假溢出的问题,通常把一维数组看成首尾相接。在循环意义下的加1运算通常用求模运算来实现。

9.当两个栈共享一存储区时,栈利用一维数组stack[1..n]表示,两栈顶指针为top[1]与top[2],则当栈1空时,top[1]为_____________,栈2空时,top[2]为_____________,栈满时为_____________。

【答案】(1)0 (2)n+1 (3)top[1]+1=top[2]

【解析】为了增加内存空间的利用率和减少溢出的可能性,由两个栈共享一片连续的内存空间时,应将两栈的栈顶分别设在这片内存空间的两端,这样,当两个栈的栈顶在栈空间的某一位置相遇时,才产生上溢,即top[1[+1=top[2]。

10.在作进栈运算时应先判别栈是否_____________;在作退栈运算时应先判别栈是否 n个,作进栈运算时发生上溢,则说明该栈的最大容量为_____________。

为了增加内存空间的利用率和减少溢出的可能性,由两个栈共享一片连续的空间时,应将两栈的_____________分别设在内存空间的两端,这样只有当______________时才产生溢出。

【答案】(1)满 (2)空 (3)n (4)栈底 (5)两栈顶指针相邻

11.在Q的链队列中,判断只有一个结点的条件是_____________。

【答案】Q->front!=NULL&&Q->front==Q->rear

【解析】只有一个结点时,队列不为空并且队头指针和队尾指针指向同一结点。

12.若用不带头结点的单链表来表示链栈S,则创建一个空栈所需要执行的操作是_____________。

【答案】S=NULL

13.无论对于顺序存储还是链式存储的栈和队列来说,进行插入和删除运算的时间复杂度均相同为_____________。

【答案】O(1)

【解析】对于栈用栈顶指针表示栈顶,而栈的插入和删除操作均在栈顶进行。对于队列用队头和队尾指针分别表示允许插入和删除的一端。

14.在顺序队列中,当尾指针等于数组的上界,即使队列不满,再作入队操作也会产生溢出,这种现象称为_____________。

【答案】假溢出

【解析】产生该现象的原因是,被删元素空间在该元素被删除后就永远得不到使用。为了克服这种现象,采用循环队列来实现。

数据结构上机实验与习题解析 亱店↘打烊oO

15.设元素1,2,3,4,5依次入栈,若要得到输出序列34251,则应进行的操作序列为PUSH(S,1),PUSH(S,2),_____________,POP(S),PUSH(S,4),POP(S),_____________,_____________,POP(S),POP(S)。

【答案】(1)PUSH(S,3) (2)POP(S) (3)PUSH(S,5)

3.3 判断题

1.即使对不含相同元素的同一输入序列进行两组不同的合法的入栈和出栈组合操作,所得的输出序列也一定相同( )

【答案】×

【解析】栈的性质为后进先出,不同的入栈出栈组合得到的输出序列不一定相同。例如:对于序列123,进行不同的入栈出栈操作,可能得到的输出序列有:123,213,321等。

2. 链式队列队满条件是尾指针加一等于头指针( )

【答案】×

【解析】链队列本身没有容量限制,所以在用户内存空间的允许范围内不会出现队满的情况。

3. 栈和队列都是线性表,只是在插入和删除时受到了一些限制( )

【答案】√

4. 循环队列也存在空间溢出问题( )

【答案】√

5. 循环队列通常用指针来实现队列的头尾相接( )

【答案】×

【解析】循环队列是解决假溢出的问题,通常把一维数组看成首尾相接。在循环意义下的加1运算通常用求模运算来实现。

3.4 应用题

1.名词解释:栈和队列

栈是只允许在一端进行插入和删除操作的线性表,允许插入和删除的一端叫栈顶,另一端叫栈底。最后插入的元素最先删除,故栈也称后进先出(LIFO)表。

队列是允许在一端插入而在另一端删除的线性表,允许插入的一端叫队尾,允许删除的一端叫队头。最先插入队的元素最先离开(删除),故队列也常称先进先出(FIFO)表。

2. 假设以S和X分别表示入栈和出栈操作,则对初态和终态均为空的栈操作可由S和X组成的序列表示(如SXSX)。

(1)试指出判别给定序列是否合法的一般规则。

(2)两个不同合法序列(对同一输入序列)能否得到相同的输出元素序列?如能得到,请举列说明。

【答案】(1)通常有两条规则。第一是给定序列中S的个数和X的个数相等;第二是从给定序列的开始,到给定序列中的任一位置,S的个数要大于或等于X的个数。(2)可以得到相同的输出元素序列。例如,输入元素为A,B,C,则两个输入的合法序列ABC和BAC均可得到输出元素序列ABC。对于合法序列ABC,我们使用本题约定的SXSXSX操作序列;对于合法序列BAC,我们使用SSXXSX操作序列。

3. 如果输入序列为123456,试问能否通过栈结构得到以下两个序列:435612和13542 6,请说明为什么不能或如何才能得到。

【答案】输入序列为123456,不能得出435612,其理由是,输出序列最后两元素是12,前面4个元素(4356)得到后,栈中元素剩12,且2在栈顶,不可能栈底元素1在栈顶元素2之前出栈。得到135426的过程如下:1入栈并出栈,得到部分输出序列1;然后2和3入栈,3出栈,部分输出序列变为:13;接着4和5入栈,5,4和2依次出栈,部分输出序列变为13542;最后6入栈并退栈,得最终结果135426。

4. 简述顺序存储队列的假溢出的避免方法及队列满和空的条件。

【答案】设顺序存储队列用一维数组q[m]表示,其中m为队列中元素个数,队列中元素在向量中的下标从0到m-1。设队头指针为front,队尾指针是rear,约定front指向队头元素的前一位置,rear指向队尾元素。当front等于-1时队空,rear等于m-1时为队满。由于队列的性质(“删除”在队头而“插入”在队尾),所以当队尾指针rear等于m-1时,若front不等于-1,则队列中仍有空闲单元,所以队列并不是真满。这时若再有入队操作,会造成假“溢出”。其解决办法有二,一是将队列元素向前“平移”(占用0至rear-front-1);二是将队列看成首尾相连,即循环队列(0..m-1)。在循环队列下,仍定义front=rear时为队空,而判断队满则用两种办法,一是用“牺牲一个单元”,即

第2部分 习题解析 亱店↘打烊oO

rear+1=front(准确记是(rear+1)%m=front,m是队列容量)时为队满。另一种解法是“设标记”方法,如设标记tag,tag等于0情况下,若删除时导致front=rear为队空;tag=1情况下,若因插入导致front=rear则为队满。

5. 若以1、2、3、4作为双端队列的输入序列,试分别求出以下条件的输出序列:

(1)能由输入受限的双端队列得到,但不能由输出受限的双端队列得到的输出序列;

(2)能由输出受限的双端队列得到,但不能由输入受限的双端队列得到的输出序列;

(3)既不能由输入受限双端队列得到,也不能由输出受限双端队列得到的输出序列。

【答案】(1)4132 (2)4213 (3)4231

3.5 程序设计题

1.设表达式以字符形式已存入数组E[n]中, # 为表达式的结束符,试写出判断表达式中括号( ( 和 ) )是否配对的C语言描述算法:EXYX(E); (注:算法中可调用栈操作的基本算法。)

【算法分析】判断表达式中括号是否匹配,可通过栈,简单说是左括号时进栈,右括号时退栈。退栈时,若栈顶元素是左括号,则新读入的右括号与栈顶左括号就可消去。如此下去,输入表达式结束时,栈为空则正确,否则括号不匹配。

【算法源代码】

int EXYX (char E[]){

/*E[]存放字符串表达式,以‘#’结束*/

char s[30]; /*s是一维数组,容量足够大,用作存放括号的栈*/

int top=0,i; /*top用作栈顶指针*/

s[top]='#'; /*‘#’先入栈,用于和表达式结束符号‘#’匹配*/

i=0; /*字符数组E的工作指针*/

while(E[i]!='#') /*逐字符处理字符表达式的数组*/

switch (E[i])

{case '(': s[++top]= '('; i++ ; break ;

case ')': if(s[top]=='('){top--; i++; break;}

else{printf("括号不配对");exit(0);}

case '#': if(s[top]=='#'){printf("括号配对\n");return (1);}

else {printf(" 括号不配对\n");return (0);} /*括号不配对*/

default : i++; /*读入其它字符,不作处理*/

}

}/*算法结束*/

2.假设以带头结点的循环链表表示队列,并且只设一个指针指向队尾结点,但不设头指针,请写出相应的入队列和出队列算法。

【算法分析】

根据队列的先进先出的性质,队列的入队操作在队尾进行,出队操作在队头进行。而题目所采用的数据结构是只设一个尾指针的循环链表。我们可以根据循环链表的特点找到头指针。

【算法源代码1】

void EnQueue (LinkList rear, ElemType x)

/* rear是带头结点的循环链队列的尾指针,本算法将元素x插入到队尾*/

{ s=(LinkList)malloc(sizeof(LNode)); /*申请结点空间*/

s->data=x; s->next=rear->next; /*将s结点链入队尾*/

rear->next=s; rear=s; /*rear指向新队尾*/

}

【算法源代码2】

void DeQueue (LinkList rear)

/* rear是带头结点的循环链队列的尾指针,本算法执行出队操作,操作成功输出队头元

素;否则给出出错信息*/

{ if(rear->next==rear) {printf("队空\n"); exit(0);}

s=rear->next->next; /*s指向队头元素*/

rear->next->next=s->next; /*队头元素出队*/

printf ("出队元素是:%d",s->data);

if(s==rear) rear=rear->next; /*空队列*/

数据结构上机实验与习题解析 亱店↘打烊oO

free(s);

}

3.设整数序列a1,a2,…,an,给出求解最大值的递归程序。

【算法分析】根据题意,本题的函数定义为:

a[1] n=1 a[n]>maxvalue(a,n-1)

maxvalue(a,n-1) a[n]<maxvalue(a,n-1)

【算法源代码】

int MaxValue (int a[],int n)

/*设整数序列存于数组a中,共有n个,本算法求解其最大值*/

{int max;

if (n==1) max=a[1];

else if (a[n]>MaxValue(a,n-1)) max=a[n];

else max=MaxValue(a,n-1);

return(max);

}

4.试将下列递归函数改写为非递归函数。

void test(int *sum)

{

int x;

scanf("%d",&x);

if(x==0) *sum=0 ;

else {test(&sum); (*sum)+=x;}

printf("%5d",*sum);

}

【算法分析】

该函数是以读入数据的顺序为相反顺序进行累加问题,可将读入数据放入栈中,等输入结束时,将栈中数据退出进行累加。累加的初值为0。

【算法源代码】

int test()

{

int x,sum=0,top=0,s[30];

scanf("%d",&x);

while (x!=0)

{s[++top]=a; scanf("%d",&x); }

printf("%5d",sum);

while (top)

{sum+=s[top--]; printf("%5d",sum); }

}

5.编写一个算法,利用栈的基本运算将指定栈中的内容进行逆转。

【算法分析】

利用两个临时栈s1和s2。先将s栈中的内容移到s1栈中,再将s1栈中的内容移到s2栈中,最后将s2栈中的内容移到s栈中,即可实现。

【算法源代码】

reverse(SqStack *s)

{SqStack *s1,*s2; /*s,s1,s2均为栈类型

ElemType x; /*栈中元素的类型,用于存储从栈中取出元素的临时变量*/

initstack(s1); /*栈的初始化*/

initstack(s2);

while(!stackempty(s)) /*如果栈不空,将s栈中的内容移到s1栈中*/

{pop(s,x); /*取栈顶元素放入变量x中*/

push(s1,x); /*将变量x入栈*/

}

while(!stackempty(s1)) /*如果栈不空,将s1栈中的内容移到s2栈中*/

{pop(s1,x);

第2部分 习题解析 亱店↘打烊oO

push(s2,x);

}

while(!stackempty(s2)) /*如果栈不空,将s2栈中的内容移到s栈中*/

{pop(s2,x);

push(s,x);

}

}

6.假设循环队列中只设rear和length来分别指示队尾元素的位置和队中元素的个数,试给出判别此循环队列的队满条件,并写出相应的入队和出队算法,要求出队时需返回队头元素。

【算法分析】

该题的关键问题是如何确定头指针,根据为指针rear和元素个数length很容易确定头指针。front=(rear-length+MAXSIZE)%MAXSIZE

【算法源代码】

#define MAXQSIZE 100 //最大队列长度

typedef int ElemType;

typedef struct {

ElemType data[MAXSIZE]; //队列存储空间

int rear; //尾指针,若队列不空,指向队列尾元素

int length; //队列内含元素的个数

}CyQueue;

int FullQueue( CyQueue *Q)

{/*判队满,队中元素个数等于空间大小*/

return Q->length==Maxsize;

}

void EnQueue( CyQueue *Q, ElemType x)

{/* 入队

if(FullQueue( Q)) {printf("队已满,无法入队");return;} */

Q->Data[Q->rear]=x;

Q->rear=(Q->rear+1)%MAXSIZE /*在循环意义上的加1*/

Q->length++;

}

ElemType DeQueue( CyQueue *Q)

{/*出队*/

int front; /*设一个临时队头指针*/

if(Q->length==0)

Error("队已空,无元素可出队");

front=(Q->rear + MAXSIZE - Q->length)%MAXSIZE;

Q->length --;

return Q->Data[front];

}

7.一个双向栈S是在同一向量空间内实现的两个栈,它们的栈底分别设在向量空间的两端。 试为此双向栈设计初始化InitStack ( S ) 、入栈Push( S , i , x) 和出栈Pop( S , i )等算法, 其中i为0 或1, 用以表示栈号。

【算法分析】

双向栈其实和单向栈原理相同,只是在一个向量空间内,好比是两个头对头的栈放在一起,中间的空间可以充分利用。

【算法源代码】

void InitStack( DuStack *S )/*初始化双向栈*/

{S->top[0] = -1;

S->top[1] = STACKSIZE;

}

int EmptyStack( DuStack *S, int i )

/*判栈空(栈号 i) */

{return (i == 0 && S->top[0] == -1|| i == 1 && S->top[1] == STACKSIZE) ;

}

数据结构上机实验与习题解析 亱店↘打烊oO

int FullStack( DuStack *S)

/*判栈满,满时肯定两头相遇*/

{return (S->top[0] == S-top1-1);

}

void Push(DuStack *S, int i, ElemType x)

/*进栈(栈号i) */

{if (FullStack( S ))

Error("Stack overflow");/*上溢、退出运行*/

if ( i == 0) S->Data[ ++ S->top0]= x; /*栈0入栈*/

if ( i == 1) S->Data[ -- S->top[1] ]= x; /* 栈1入栈*/

}

ElemType Pop(DuStack *S, int i)

/*出栈(栈号i) */

{if (EmptyStack ( S,i) )

Error("Stack underflow");/*下溢退出*/

if( i==0 )

return ( S->Data[ S->top0--] );/*返回栈顶元素,指针值减1*/

if( i==1 )

return ( S->Data[ S->top[1] ++] ); /*该栈是以另一端为底的,所以指针加1*/

}

8.回文是指正读反读均相同的字符序列,如"abba"和"abdba"均是回文,但"good"不是回文。设计一个算法判定给定的字符向量是否为回文。(提示:将一半字符入栈)

【算法源代码】

void sympthy(LinkList head, stack *s)/*判断长为n的字符串是否中心对称*/

{ int i=1;

LinkList p=head->next;

while(i<=n/2) /* 前一半字符进栈*/

{ push(s,p->data); p=p->next; }

if(n%2!==0) p=p->next;/* 奇数个结点时跳过中心结点*/

while(p&&p->data==pop(s)) p=p->next;

if (p==NULL)

printf("链表中心对称");

else printf("链表不是中心对称");

} /* 算法结束*/

9.用标志位方式设计出在循环队列中进行插入和删除运算的算法。

【算法分析】

可引入标志位flag,且规定当flag=0时表示队列空,当flag=1时表示队列非空。同时设front,rear和MAXSIZE分别为队头指针,队尾指针和队列的长度。其中,队头指针指向队头元素所在的实际存储单元的前一个位置,队尾指针指向队尾元素所在的位置。从而可得队满的条件是:(rear==front)&&(flag==1)

【算法源代码】

int flag; /*设置全局变量flag作为标志位*/

enqueue(SqQueue*sq,ElemType x) {

/*将x插入循环队列sq中,sq具有队头和队尾指针*/

if((flag==1)&&(sq->rear==sq->front)) /*判断队满*/

printf("queue is full!\n");

else

{sq->rear=(sq->rear+1)%MAXSIZE;

sq->data[sq->rear]=x;

}

if(flag==0) flag=1;

}

ElemType dequeue(SqQueue*sq) /*删除队列sq的队头元素,并返回该元素*/

{ElemType x;

if(flag==0) printf("queue is empty!\n"); /*判断队空*/

第2部分 习题解析 亱店↘打烊oO

else

{sq->front=(sq->front+1)%MAXSIZE;

x=sq->data[sq->front];

}

if(sq->front==sq->rear) flag=0;

return x;

}

第4章 串

4.1 选择题

1.下面关于串的的叙述中,哪一个是不正确的?( )

A)串是字符的有限序列

B)空串是由空格构成的串

C)模式匹配是串的一种重要运算

D)串既可以采用顺序存储,也可以采用链式存储

【答案】B

【解析】空串是不含任何字符的串,即空串的长度是零。空格串是由空格组成的串,其长度等于空格的个数。

2.设有两个串p和q,其中q是p的子串,求q在p中首次出现的位置的算法称为( )

A)求子串 B)联接 C)匹配 D)求串长

【答案】C

3.若串s="software",其子串个数是( )

A)8 B)37 C)36 D)9

【答案】C

【解析】s的长度为8,长度为8的子串有1个,长度为7的子串有2个,长度为6的子串有3个,长度为5的子串有4个,…,长度为1的子串有8个,共有(1+8)*8/2=36个。

4.串的长度是指( )

A)串中所含不同字母的个数

B)串中所含字符的个数

C)串中所含不同字符的个数

D)串中所含非空格字符的个数

【答案】B

5.若串S1="ABCDEFG",S2="9898",S3="###",S4="012345",则执行concat(replace(S1,substr(S1,length(S2),length(S3)),S3),substr(S4,index(S2, '8'),length(S2)))其结果为( )

A)ABC###G0123 B)ABCD###2345

C)ABC###G2345 D)ABC###G1234

【答案】D

【解析】函数concat(x,y)返回x和y的连接串,substr(s,i,j)返回串s的从序号i的字符开始的j个字符组成的子串,length(s)返回串s的长度。replase(s,t,v)用v替换s中出现的所有与t相等的子串,index(s,t,i)当s中存在与t值相同的子串时,返回它在s中的第i个字符之后第一次出现的位置。

substr(S1,length(S2),length(S3))=substr(S1,4,3)= "DEF";

replase(S1,substr(S1,length(S2),length(S3)),S3)=replase(S1, "DEF",S3)= "ABC###G";

substr(S4,index(S2, '8'),length(S2))=substr(S4,2,4)= "1234";

concat(replace(S1,substr(S1,length(S2),length(S3)),S3),substr(S4,index(S2,

'8'),length(S2)))=concat("ABC###G", "1234")= "ABC###G1234"

4.2 填空题

1.空格串是指_____________,其长度等于_____________。

【答案】(1)由空格字符(ASCII值32)所组成的字符串 (2)空格个数

2.组成串的数据元素只能是_____________。

【答案】字符

【解析】串是一种特殊的线性表,其特殊性在于串中的元素只能是字符型数据。

数据结构上机实验与习题解析 亱店↘打烊oO

3.设正文串长度为n,模式串长度为m,则串匹配的KMP算法的时间复杂度为_____________。

【答案】O(m+n)

【解析】朴素的模式匹配(Brute-Force)时间复杂度是O(m*n),KMP算法有一定改进,时间复杂度达到O(m+n)。

4.两个字符串相等的充分必要条件是_____________。

【答案】两串的长度相等且两串中对应位置上的字符也相等。

5.一个字符串中_____________称为该串的子串 。

【答案】任意个连续的字符组成的子序列

4.3 判断题

1.KMP算法的特点是在模式匹配时指示主串的指针不会变小( )

【答案】√

【解析】KMP算法的改进在于:每当一趟匹配过程中出现字符比较不相等时,不需回溯主串指针,而是利用已经得到的“部分匹配”的结果将模式向右“滑动”尽可能远的一段距离后,继续进行比较。

2.串是一种数据对象和操作都特殊的线性表( )

【答案】√

【解析】串是一种特殊的线性表,其特殊性在于串中的元素只能是字符型数据。字符型数据的操作符合字符型数据的操作规范,具有它的特殊性。

3.如果一个串中的所有字符均在另一串中出现,那么说明前者是后者的子串( )

【答案】×

【解析】一个字符串中任意个连续的字符组成的子序列称为该串的子串,注意其中字符的连续性。

4.4 应用题

1.描述以下概念的区别:空格串与空串。

【答案】空格是一个字符,其ASCII码值是32。空格串是由空格组成的串,其长度等于空格的个数。空串是不含任何字符的串,即空串的长度是零。

2.设S1,S2为串,请给出使S1/*S2=S2/*S1成立的所有可能的条件(/*为连接符)。

【答案】

(1)s1和s2均为空串;

(2)两串之一为空串;

(3)两串串值相等(即两串长度相等且对应位置上的字符相同)。

(4)两串中一个串长是另一个串长(包括串长为1仅有一个字符的情况)的数倍,而且长串就好象是由数个短串经过连接操作得到的。

3.已知:s =" (xyz)+*",t =" (x+z)*y"。试利用联结、求子串和置换等基本运算,将 s 转化为 t 。

【答案】本题有多种解法,下面是其中的一种:

(1) s1=substr(s,3,1) /*取出子串:"y"

(2) s2=substr(s,6,1) /*取出子串:"+"

(3) s3=substr(s,1,5) /*取出子串:" (xyz) "

(4) s4=substr(s,7,1) /*取出子串:"*"

(5) s5=replace(s3,3,1,s2)/*形成部分串:" (x+z) "

(6) s=s5/*s4/*s1 /*形成串t即" (x+z)*y"

【解析】题中所给操作的含义如下:

/*:连接函数,将两个串连接成一个串

substr(s,i,j):取子串函数,从串s的第i个字符开始,取连续j个字符形成子串

replace(s1,i,j,s2):置换函数,用s2串替换s1串中从第i个字符开始的连续j个字符

4.5 算法设计题

1.设s、t为两个字符串,分别放在两个一维数组中,m、n分别为其长度,判断t是否为s的子串。如果是,输出子串所在位置(第一个字符),否则输出0。

【算法分析】

判断字符串t是否是字符串s的子串,称为串的模式匹配,其基本思想是对串s和t各设一个指针i和j,i的值域是0..m-n,j的值域是0..n-1。初始值i和j均为0。模式匹配从s0和t0开始,若s0==t0,则i和j指针增加1,若在某个位置si!=tj,则主串指针i回溯到i=i-j+1,j仍从0开始,进行下一轮的比较,直到匹配成功(j>n-1),返回子串在主串的位置(i-j)。否则,当i>m-n则为匹配失败。

第2部分 习题解析 亱店↘打烊oO

【算法源代码】

int index(char s[ ], char t[ ],int m,int n)

{int i=0,j=0;

while (i<=m-n && j<=n-1)

if (s[i]==t[j]){i++;j++;} /*对应字符相等,指针后移*/

else {i=i-j+1;j=0;} /*对应字符不相等,i回溯,j仍为0*/

if(i<=m-n && j==n)

{printf("t在s串中位置是%d",i-n+1);

return(i-n+1);

}/*匹配成功*/

else return(0); /*匹配失败*/

}

2.函数void insert(char*s,char*t,int pos)将字符串t插入到字符串s中,插入位置为pos。请用c语言实现该函数。假设分配给字符串s的空间足够让字符串t插入。(说明:不得使用任何库函数)

【算法分析】

本题是字符串的插入问题,要求在字符串s的pos位置,插入字符串t。首先应查找字符串s的pos位置,将第pos个字符到字符串s尾的子串向后移动字符串t的长度,然后将字符串t复制到字符串s的第pos位置后。

对插入位置pos要验证其合法性,小于1或大于串s的长度均为非法,因题目假设给字符串s的空间足够大,故对插入不必判溢出。

【算法源代码】

void insert(char *s,char *t,int pos)

/*将字符串t插入字符串s的第pos个位置*/

{

int i=1,x=0,j; char *p=s,*q=t; /*p,q分别为字符串s和t的工作指针*/

if(pos<1)

{printf("pos参数位置非法\n");exit(0);}

while(*p!='\0'&&i<pos) {p++;i++;} /*查pos位置*/

if(*p=='/0')

{printf("%d位置大于字符串s的长度",pos);exit(0);}

else /*查找字符串的尾*/

while(*p!= '/0')

{p++; i++;} /*查到尾时,i为字符'\0'的下标,p也指向'\0'*/

while(*q!= '\0')

{q++; x++; } /*查找字符串t的长度x,循环结束时q指向'\0'*/

for(j=i;j>=pos ;j--)

{*(p+x)=*p; p--;}/*串s的pos后的子串右移,空出串t的位置*/

q--; /*指针q回退到串t的最后一个字符

for(j=1;j<=x;j++) *p--=*q--; /*将t串插入到s的pos位置上*/

}

3.设计一个算法,统计在输入字符串中各个不同字符出现的频度。(字符串中的合法字符为'A'-'Z'这26个字母和'0'-'9'这10个数字)。

【算法分析】

由于字母共26个,加上数字符号10个共36个,所以设一长36的整型数组,前10个分量存放数字字符出现的次数,余下存放字母出现的次数。从字符串中读出数字字符时,字符的ASCII代码值减去数字字符'0'的ASCII代码值,得出其数值(0..9),字母的ASCII代码值减去字符'A'的ASCII代码值加上10,存入其数组的对应下标分量中。遇其它符号不作处理,直至输入字符串结束。

【算法源代码】

void Count()

/*统计输入字符串中数字字符和字母字符的个数*/

{int i,num[36];

char ch;

for(i=0;i<36;i++)num[i]=0;/* 初始化*/

本文来源:https://www.bwwdw.com/article/qnnj.html

Top