数据结构第二章线性表顺序表练习题及答案P40(2)

it2024-07-12  39

数据结构第二章线性表顺序表练习题及答案P40(2)

文章目录

数据结构第二章线性表顺序表练习题及答案P40(2)15.已知两个链表A和B分别表示两个集合,其元素递增排列。编制函数,求A与B的交集,并存放于A链表中16.两个整数序列A=a1,a2,a3,…,am和B=b1,b2,b3…,bn已经存入两个单链表中,设计一个算法,判断序列B是否是序列A的连续子序列17.设计一个算法用于判断带头结点的循环双链表是否对称18.有两个循环单链表,链表头指针分别为h1和h2,编写一个函数将链表h2链接到链表h1之后,要求链接后的链表仍保持循环链表形式19.(未看)设有一个带头结点的循环单链表,其结点值均为正整数,设计一个算法,反复找出单链20.(未看)设头指针为L的带有表头结点的非循环双向链表,其每个结点中除有Pred(前驱21.(未看)【2009统考真题】已知一个带有表头结点的单链表,结点结构为[data][link],22(未看)【2012统考真题】假定采用带头结点的单链表保存单词,当两个单词有相同的后缀时,23.(未看)【2015统考真题】用单链表保存m个整数,结点的结构为[data][link],且 I datal≤n24.(未看)设计一个算法完成以下功能:判断一个链表是否有环,如果有,找出环的入口点并返回,否则返回NULL25.(未看)【2019统考真题】设线性表L=(a1,a2,a3,...,an-2,an-1,an)采用带头结点的单链表保存,链表中的结点定义如下:

15.已知两个链表A和B分别表示两个集合,其元素递增排列。编制函数,求A与B的交集,并存放于A链表中

算法思想:采用归并的思想,设置两个工作指针pa和pb,对两个链表进行归并扫描, 只有同时出现在两集合中的元素才链接到结果表中且仅保留一个,其他的结点全部释放, 当一个链表遍历完毕后,释放另一个表中剩下的全部结点。

LinkList Union(LinkList &la,LinkList &lb){ LNode *pa=la->next,*pb=lb->next,*head,q; //设工作指针分别为pa和pb,q为要释放的结点 head=la; //head为la的表头结点 while(pa!=NULL && pb!=NULL){ if(pa->data < pb->data){ //若A中当前结点值小于B中当前结点值 q=pa; pa=pa->next; //后移结点 free(q); //释放A中当前结点 } else if(pa->data > pb->data){ //同上 q=pb; pb=pb->data; free(q); } else{ //交集并入结果表中 head->next=pa; //A中结点链接到结果表 head=pa; pa=pa->next; q=pb; //B中释放结点 pb=pb->next; free(q); } } while(pa){ //B已遍历完,A未完 q=pa; pa=pa->next; free(q); //释放A中剩余结点 } while(pb){ //同上 q=pb; pb=pb->next; free(q); } head->next=NULL; // 置结果链表尾指针为NULL free(lb); //释放B表的头结点 return la; }

16.两个整数序列A=a1,a2,a3,…,am和B=b1,b2,b3…,bn已经存入两个单链表中,设计一个算法,判断序列B是否是序列A的连续子序列

算法思想:因为两个整数序列已存入两个链表中,操作从两个链表的第一个结点开始,若对 应数据相等,则后移指针:若对应数据不等,则A链表从上次开始比较结点的后继开始,B链表 仍从第一个结点开始比较,直到B链表到尾表示匹配成功。A链表到尾而B链表未到尾表示失败 操作中应记住A链表每次的开始结点,以便下次匹配时好从其后继开始。

int Pattern(LinkList A,LinkList B){ LNode *p=A,*pre,*q=B; //p为A链表的工作指针,q为B链表的工作指针,本题假设A和B均无头结点 pre=p; //pre记住每趟比较中A链表的开始结点 while(p&&q){ if(p->data==q->data){ p=p->next; q=q->next; } else{ pre=pre->next; p=pre; //A链表新的开始比较结点 q=B; //q从B链表第一个结点开始 } } if(q==NULL){ //B已经结束 return 1; //1说明B是A的子序列,0不是A的子序列 } else{ return 0; } }

17.设计一个算法用于判断带头结点的循环双链表是否对称

算法思想:让p从左向右扫描,q从右向左扫描,直到它们指向同一结点(p==q,当循环双 链表中结点个数为奇数时)或相邻(p->next=q或q-> prior=p,当循环双链表中结点个数为偶 数时)为止,若它们所指结点值相同,则继续进行下去,否则返回0。若比较全部相等,则返回1

int Symmetry(DLinkList L){ DNode *p=L->next,*q=L->prior; //两头工作指针 while(p!=q || q->next!=p){ //循环工作条件 if(p->data==q->data){ //所指结点值相同则继续比较 p=p->next; q=q->prior; } else{ return 0; //否则,返回0 } } return 1; //比较结束后返回1 }

18.有两个循环单链表,链表头指针分别为h1和h2,编写一个函数将链表h2链接到链表h1之后,要求链接后的链表仍保持循环链表形式

算法思想:先找到两个链表的尾指针,将第一个链表的尾指针与第二个链表的头结点链接起 来,再使之成为循环的。

LinkList Link(LinkList &h1,LinkList &h2){ LNode *p,*q; //分别指向两个链表的尾结点 p=h1; while(p->next!=NULL){ //寻找h1的尾结点 p=p->next; } q=h2; while(q->next!=NULL){ //寻找h2的尾结点 q=q->next; } p->next=h2; //将h2链接到h1之后 q->next=h1; //将h1的尾结点指向h1 return h1; }

19.(未看)设有一个带头结点的循环单链表,其结点值均为正整数,设计一个算法,反复找出单链

表中结点值最小的结点并输出,然后将该结点从中删除,直到单链表空为止,再删除表 头结点。 算法思想:对于循环单链表L,在不空时循环:每循环一次查找一个最小结点(由minp指向最小值结 点, minpre指向其前驱结点)并删除它。最后释放头结点

void Del_All(LinkList &L){ LNode *minpre,*minp,*pre,*p; while(L->next!=L){ p=L->next; pre=L; minp=p;minpre=pre; while(p!=L){ if(p->data < minp->data){ minp=p; minpre=pre; } pre=p; p=p->next; } printf("%d",minp->data); minpre->next=minp->next; free(minp); } free(L); }

20.(未看)设头指针为L的带有表头结点的非循环双向链表,其每个结点中除有Pred(前驱

指针)、data(数据)和next(后继指针)域外,还有一个访问频度域freq.在 链表被启用前,其值均初始化为零。每当在链表中进行一次 Locate(L,x)运算时, 令元素值为ⅹ的结点中freq域的值增1,并使此链表中结点保持按访问频度非增 (递减)的顺序排列,同时最近访问的结点排在频度相同的结点前面,以便使频繁 访问的结点总是靠近表头。试编写符合上述要求的 Locate(L,x)运算的算法,该 运算为函数过程,返回找到结点的地址,类型为指针型 算法思想:首先在双向链表中查找数据值为x的结点,查到后,将结点从链表上摘下,然后 再顺着结点的前驱链查找该结点的插入位置(频度递减,且排在同频度的第一个,即向前找到第 比它的频度大的结点,插入位置为该结点之后),并插入到该位置。

DLinkList Locate(DLinkList &L,ElemType x){ DNode *p=L->next,*q; //p为工作指针,q为p的前驱,用于查找插入位置 while(p && p->data!=x){ p=p->next; //查找值为x的结点 } if(!p){ printf("不存在值为x的结点\n"); exit(0); } else{ p->freq++; //令元素值为x的结点的freq域+1 if(p->next!=NULL){ p->next-pred=p->pred; } p->pred->next=p->next; //将p结点从链表上摘下 q=p->nextpred; //以下查找p结点的插入位置 while(q!=L && q->freq <= p->freq){ q=q->pred; } p->next=q->next; q->next->pred=p; //将p结点插入,一定是排在同一频率的第一个 p->pred=q; q->next=p; } return p; //返回值为x的结点的指针 }

21.(未看)【2009统考真题】已知一个带有表头结点的单链表,结点结构为[data][link],

假设该链表只给出了头指针1ist.在不改变链表的前提下,请设计一个尽可能高效的 算法,查找链表中倒数第k个位置上的结点(k为正整数)。若查找成功,算法输出该结 的data域的值,并返回1;否则,只返回0.要求: 1)描述算法的基本设计思想 2)描述算法的详细实现步骤 3)根据设计思想和实现步骤,采用程序设计语言描述算法(使用C、C++或Java语言 实现)关健之处请给出简要注释 1)算法的基本设计思想如下 问题的关键是设计一个尽可能高效的算法,通过链表的一次遍历,找到倒数第k个结点的位 置。算法的基本设计思想是:定义两个指针变量p和q,初始时均指向头结点的下一个结点(链 表的第一个结点),p指针沿链表移动:当p指针移动到第k个结点时,q指针开始与p指针同 步移动;当p指针移动到最后一个结点时,q指针所指示结点为倒数第k个结点。以上过程对链 表仅进行一遍扫描。 2)算法的详细实现步骤如下 ① count=0,p和q指向链表表头结点的下一个结点 ②若p为空,转⑤ ③若 count等于k,则q指向下一个结点:否则, count= count+1 ④p指向下一个结点,转② ⑤若 count等于k,则查找成功,输出该结点的data域的值,返回1:否则,说明k值 超过了线性表的长度,查找失败,返回0 ⑤算法结束

typedef int ElemType; //链表数据的类型定义 typedef struct LNode{ //链表结点的结构定义 ElemType data; //结点数据 struct LNode *link; //结点链接指针 }LNode,*LinkList; int Search_k(LinkList list,int k){ //查找链表list倒数第k个结点,并输出该结点data域的值 LNode *p=list->link,*q=list->link; //指针p、q指示第一个结点 int count=0; while(p!=NULL){ //遍历链表直到最后一个结点 if(count<k) count++; //计数,若count<k只移动p else q=q->link; p=p->link; //之后让p、q同步移动 } if(count<k){ return 0; //若查找失败返回0 } else{ //否则打印并返回1 printf("%d",q->data); return 1; } }

22(未看)【2012统考真题】假定采用带头结点的单链表保存单词,当两个单词有相同的后缀时,

可共享相同的后缀存储空间,例如,"loading"和"being"的存储映像如下图所示

设str1和str2分别指向两个单词所在单链表的头结点,链表结点结构为[data][next] 请设计一个时间上尽可能高效的算法,找出由str1和str2所指向两个链表共同后缀 的起始位置(如图中字符i所在结点的位置P).要求 1)给出算法的基本设计思想 2)根据设计思想,采用C或C艹或Java语言描述算法,关键之处给出注释 3)说明你所设计算法的时间复杂度. 1)算法的基本设计思想如下 ①分别求出str1和str2所指的两个链表的长度m和n ②将两个链表以表尾对齐:令指针P、q分别指向atx1和atr2的头结点,若m2n,则 指针p先走,使p指向链表中的第m=n+1个结点:若m<n,则使q指向链表中的第 n一m+1个结点,即使指针p和q所指的结点到表尾的长度相等 ③反复将指针p和q同步向后移动,当p、q指向同一位置时停止,即为共同后级的起始 位置,算法结束。

typedef struct Node{ char data; struct Node *next; }SNode; init listlen(SNode *head){ //求链表长度的函数 int len=0; while(head->next!=NULL){ len++; head=head->next; } return len; } SNode* find_addr(SNode *str1,SNode *str2){ int m,n; SNode *p,*q; m=listlen(str1); //求str1的长度 n=listlen(str2); //求str2的长度 for(p=str1;m>n;m--){ //若m>n,使p指向链表中的第m-n+1个结点 p=p->next; } for(q=str2;m<n;n--){ //若m<n,使p指向链表中的第n-m+1个结点 q=q->next; } while(p->next!=NULL && p->next!=q->next){ //将指针p和q同步向后移动 p=p->next; q=q->next; } return p->next; //返回共同后缀的起始地址 }

23.(未看)【2015统考真题】用单链表保存m个整数,结点的结构为[data][link],且 I datal≤n

(n为正整数).现要求设计一个时间复杂度尽可能高效的算法,对于链表中data的绝对值 相等的结点,仅保留第一次出现的结点而删除其余绝对值相等的结点。例如,若给定的单 链表head如下:

要求: 1)给出算法的基本设计思想 2)使用C或C艹语言,给出单链表结点的数据类型定义 3)根据设计思想,采用C或C+语言描述算法,关键之处给出注释 4)说明你所设计算法的时间复杂度和空间复杂度

1)算法的基本设计思想 ·算法的核心思想是用空间换时间。使用辅助数组记录链表中已出现的数值,从而只需对链 表进行一趟扫描 因为|data|<=n,故辅助数组q的大小为n+1,各元素的初值均为0。依次扫描链表中的 各结点,同时检查q[ |data|]的值,若为0则保留该结点,并令 g[Idatal1=1;否则将 该结点从链表中删除。

typedef struct node{ int data; struct node *link; }NODE; Typedef NODE *PNODE; void func(PNODE h,int n){ PNODE p=h,r; int *q,m; q=(int*)malloc(sizeof(int)*(n+1)); //申请n+1个位置的辅助空间 for(int i=0;i<n+1;i++){ //数组元素初值置为 *(q+i)=0; } while(p->link!=NULL){ m=p->link->data>0? p->link->data:-p->link->data; if(*(q+m)==0){ //判断该结点的data是否已出现过 *(q+m)=1; //首次出现 p=p->link; //保留 } else{ //重复出现 r=p->link; //删除 p->link=r->link; free(r); } } free(q); }

24.(未看)设计一个算法完成以下功能:判断一个链表是否有环,如果有,找出环的入口点并返回,否则返回NULL

1)算法的基本设计思想 设置快慢两个指针分别为fat和s1ow,初始时都指向链表头head,slow每次走一步, 即s1ow=s1ow->next;fast每次走两步,即fast=faat->next->next,由于fast比s1ow 走得快,如果有环,fast一定会先进入环,而s1ow后进入环,当两个指针都进入环后,经过 若干次操作后两个指针定能在环上相遇。这样就可以判断一个链表是否有环 如下图所示,当s1ow刚进入环时,faat早已进入环。因为fat每次比s1ow多走一步 且fast与s1ow的距离小于环的长度,所以fast与s10w相遇时,s1ow所走的距离不超过环 的长 如下图所示,设头结点到环的入口点的距离为a,环的入口点沿着环的方向到相遇点的距离 为x,环长为x,相遇时fast绕过了n圈,则有2(a+x)=a+nx+x,即a=n*x-x,显然从头结点到环的入口点的距离等于n倍的环长减去 环的入口点到相遇点的距离。因此可设置两个指针,一个指向head,一个指向相遇点,两个指 针同步移动(均为一次走一步),相遇点即为环的入口点

LNode* FindLoopStart(LNode *head){ LNode *fast=head,*slow=head; //设置快慢两个指针 while(slow!=NULL && fast->next!=NULL){ slow=slow->next; //每次走一步 fast=fast->next->next; //每次走两步 if(slow==fast)break; //相遇 } if(slow==NULL || fast->next==NULL){ return NULL; //没有环,返回NULL } LNode *p1=head,*p2=slow; //分别指向开始点、相遇点 while(p1!=p2){ p1=p1->next; p2=p2->next; } return p1; //返回入口点 }

25.(未看)【2019统考真题】设线性表L=(a1,a2,a3,…,an-2,an-1,an)采用带头结点的单链表保存,链表中的结点定义如下:

typedef struct node { int data; struct node*next; }NODE;

请设计一个空间复杂度为O(1)且时间上尽可能高效的算法,重新排列L中的各结点,得 到线性表L=(a1,an,a2,an-1,a3,an-2,…),要求: 1)给出算法的基本设计思想 2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释 3)说明你所设计的算法的时间复杂度

1)算法的基本设计思想 先观察L=(a,a2,a,…,an2,an1,a)和L=(,a,、an1,2,2),发现L是由L摘取第 个元素,再摘取倒数第一个元素…依次合并而成的。为了方便链表后半段取元素,需要先将 L后半段原地逆置[题目要求空间复杂度为O1),不能借助栈,否则每取最后一个结点都需要遍 历一次链表。①先找出链表L的中间结点,为此设置两个指针p和q,指针p每次走一步,指针 q每次走两步,当指针q到达链尾时,指针p正好在链表的中间结点:②然后将L的后半段结点 原地逆置。③从单链表前后两段中依次各取一个结点,按要求重排。

void change_list(NODE* h){ NODE *p,*q,*r,*s; p=q=h; while(q->next!=NULL){ //寻找中间结点 p=p->next; //p走一步 q=q->next; if(q->next!=NULL) q=q->next; //q走两步 } q=p->next; //p所指结点为中间结点,q为后半段链表的首结点 p->next=NULL; while(q!=NULL){ //将链表后半段逆置 r=q->next; q->next=p->next; p->next=q; q=r; } s=h->next; //s指向前半段的第一个数据结点,即插入点 q=p->next; //q指向后半段的第一个结点 p->next=NULL; while(q!=NULL){ //将链表后半段的结点插入到指定位置 r=q->next; //r指向后半段的下一个结点 q->next=s->next; //将q所指结点插入到s所指结点之后 s->next=q; s=q->next; //s指向前半段的下一个插入点 q=r; } }
最新回复(0)