提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
内容主要涉及到链表的相关题目
一、设计递归算法,删除不带头结点的单链表中所有值为X的结点。
void DG_Delete(Linklist &L,Linklist &pre,Linklist &p,int x){
if (L->data==x){
L=L->next;
free(p);
pre=L;
p=L;
}
if (p){
if (p->data==x){
pre->next=p->next;
free(p);
p=pre->next;
} else{
pre=p;
p=p->next;
}
DG_Delete(L,pre,p,x);
}
}
二、在带头结点的单链表L中,删除所有值为X的结点,并释放其空间,假设值为X的结点不唯一,试编写算法以实现上述操作。(使用了递归)
void Delete(Linklist &pre,Linklist &p,int x){
if (p){
if (p->data==x){
pre->next=p->next;
free(p);
p=pre->next;
} else{
pre=p;
p=p->next;
}
Delete(pre,p,x);
}
}
三、设L为带头结点的单链表,编写算法实现从尾到头反向输出每个结点的值。(递归)
void reverse_head(Linklist &L){
if(L){
reverse_head(L->next);
printf("%d",L->data);
}
}
四、试编写在带头结点的单链表L中删除一个最小值结点的高效算法(假设最小值结点是唯一的)。
方法①
/*
* 先令链表中第一个结点(不是头结点)的值为最小值min,依次把最小值与后面的结点比较
* 如果有结点的值比min还要小,则令min等于该值,指针的位置也要相应的调整
*/
void delete_min(Linklist &L){
Lnode *p=L,*q=p->next;
int min=q->data;
while (q->next){
if (min>q->next->data){
min=q->next->data;
p=q;
q=q->next;
} else {q=q->next;}
}
q=p->next;
p->next=q->next;
free(q);
}
方法②
/*
* 设定两对指针,指针minpre和min指向最小值结点的前一个结点和最小值结点,指针pre和q用作遍历
* 进行比较q和min指针所指的结点的值,如果q->data大于min->data,则pre和p向后遍历
* 如果q->data小于min->data,此时q所指的结点的值可能为最小值,令min=q,minpre=pre
* 直到q指向NULL为止
*/
void delete_min_2(Linklist &L){
Lnode *minpre=L,*min=minpre->next;
Lnode *pre=L,*q=pre->next;
while (q){
if (min->data>q->data){
min=q;
minpre=pre;
} else{
q=q->next;
pre=pre->next;
}
}
minpre->next=min->next;
free(min);
return;
}
五、试编写算法将带头结点的单链表就地逆置,所谓“就地”是指辅助空间复杂度为 O(1)。
/*
* 利用头插法思想,定义一个指针指向L->next,再将原来的单链表从头结点之后断开,依次将结点以头插法的形式插入
*/
void reverse1(Linklist &L){
Lnode *p=L->next,*q;
L->next=NULL;
while (p){
q=p->next;
p->next=L->next;
L->next=p;
p=q;
}
}
六、有一个带头结点的单链表L,设计一个算法使其元素递增有序。
方法①:选择排序
/*
*新建立一个带头结点的单链表M,每次拿出L单链表中一个最小的数,按照尾插法插入M中,最后free(L),输出M
*/
void selectSort_list(Linklist &M,Linklist &L){
Lnode *m=M,*pre,*p,*minpre,*minp;
while (L->next){
pre=L,p=pre->next;
minpre=L,minp=minpre->next;
while (p){
if (minp->data>p->data){
minp=p;
minpre=pre;
} else{
p=p->next;
pre=pre->next;
}
}
minpre->next=minp->next;
minp->next=m->next;
m->next=minp;
m=minp;
}
m->next=NULL;
free(L);
}
方法①:插入排序
有序区和无序区说明:
/*
* 思想:定义两个指针,分别指向第一个和第二个结点,再将头节点断开设定为有序区,每次从无序区内拿来一个数插入到有序区中比他大的数前面
*/
void insertSort(Linklist &L){
Lnode *p=L->next,*q=p,*u,*v;
L->next=NULL;
while (p!=NULL){
u=L;
v=u->next;
q=q->next;
while (v!=NULL&&v->data<=p->data){
u=v;
v=v->next;
}
u->next=p;
p->next=v;
p=q;
}
}
七、设在一个带表头结点的单链表中所有元素结点的数据值无序,试编写一个函数,删除表中所有介于给定的两个值(作为函数参数给出)之间的元素的元素(若存在)。
/*
* 定义两个指针(便于删除)依次比较单链表中每个结点的值是否在给出的两数之间
* 如果不在则两个指针向后遍历
* 如果在则先让pre->next=p->next,然后free(p)
*/
void delete7(Linklist &L,int n,int m){
if (n>m){
return;
}
Lnode *pre=L,*p=pre->next;
while (p){
if(p->data>n&&p->data<m){
pre->next=p->next;
free(p);
p=pre->next;
} else{
pre=pre->next;
p=p->next;
}
}
}
八、给定两个单链表,编写算法找出两个链表的公共结点(带头结点)。
/*
* 公共节点即为指针所指的地址相等
* 先算出两单链表的长度,如果不相等则让长度长的单链表先遍历
* 直到两个链表剩余的长度相等,才开始同时遍历,直到地址相等遍历结束,返回地址
*/
Linklist common(Linklist S,Linklist L){
int a= find_Link_nu(S);
int b= find_Link_nu(L);
int n;
Lnode *s=S->next,*l=L->next;
if (a>b){
n=a-b;
for (int i = 0; i < n; ++i) {
s=s->next;
}
} else{
n=b-a;
for (int i = 0; i < n; ++i) {
l=l->next;
}
}
while (s!=l){
s=s->next;
l=l->next;
}
return s;
}
九、给定一个带表头结点的单链表,设 head 为头指针,结点结构为(data,next),data为整型元素,next 为指针,试写出算法:按递增次序输出单链表中各结点的数据元素,并释放结点所占的存储空间(要求:不允许使用数组作为辅助空间)。
/*
* 定义p和q两指针,p=head,q=p->next
* 找到每次遍历的最小值输出并释放空间,下一次循环重新从上一次遍历之后剩余的数中找最小值
*/
void delete_min9(Linklist &head){
while (head->next){
Lnode *p=head,*q=p->next;
int min=q->data;
while (q->next){
if (min>q->next->data){
min=q->next->data;
p=q;
q=q->next;
} else {q=q->next;}
}
printf("%d",min);
q=p->next;
p->next=q->next;
free(q);
}
}
十、.将一个带头结点的单链表A分解为两个带头结点的单链表A和B,使得A表中含有原表中序号为奇数的元素,而B表中含有原表中序号为偶数的元素,且保持其相对顺序不变。
/*
* 新建一个带头结点的单链表B
* 定义一个count用于判断是原表的奇数结点还是偶数结点
* 将偶数结点以尾插法的方式插入到B链表中
*/
void apart(Linklist &A,Linklist &B){
int count=1;
B->next=NULL;
Lnode *a=A,*b=B,*aa=a->next,*bb=b;
while (aa){
if (count%2!=0){
a=aa;
aa=aa->next;
count++;
} else{
a->next=aa->next;
bb->next=aa;
bb=aa;
aa->next=NULL;
aa=a->next;
count++;
}
}
}
十一、【2019 统考真题】设线性表 L=(a1,a2,a3,…an)采用带头结点简单链表报存。链表中的结点定义如下
请设计一个空间复杂度为 O(1)且时间上尽可能高效的算法,重新排列 L中的各结点,得到线性表 L’=(a1,an,a2,an-1,a3,an-2,…)。
/*
* 新建带头结点的单链表M
* 找到第n/2个结点,从该结点之后断开,断开后的结点按照头插法插入到M中
* 之后从L的头结点后断开,将a1,a2,...an/2的结点尾插法插入到L中奇数的位置
* 将M链表里面的结点按照尾插法,插入到L的偶数位置
* (奇数还是偶数位置用count变量记录)
* 之后判断有哪个链表还没有插入完整,再向后插入L中即可
*/
void new_L(Linklist &L,int n,Linklist &M){
Lnode *p=L->next;
for (int i = 1; i < n/2; ++i) {
p=p->next;
}
Lnode *q=p->next;
p->next=NULL;
p=q;
while (p){
q=p->next;
p->next=M->next;
M->next=p;
p=q;
}
Lnode *pre=L->next,*r,*m=M->next,*l=L;
L->next=NULL;
int count=1;
while (pre&&m){
if (count%2!=0){
r=pre->next;
pre->next=l->next;
l->next=pre;
l=pre;
pre=r;
count++;
} else{
r=m->next;
m->next=l->next;
l->next=m;
l=m;
m=r;
count++;
}
}
while (pre){
r=pre->next;
pre->next=l->next;
l->next=pre;
l=pre;
pre=r;
}
while (m){
r=m->next;
m->next=l->next;
l->next=m;
l=m;
m=r;
}
}
十二、在一个递增有序的带头结点线性表中,有数值相同的元素存在。若存储方式为单链表)设计算法去掉数值相同的元素,使表中不再有重复的元素,例如 (7,10,10,21,30,42,42,42,51,70)将变成(7,10,21,30,42,51,70)
/*
* 将单链表从头结点后断开,形成两个单链表L和链表中第一个结点(不是头结点)到左后一个结点的链表
* 定义两个指针,p和q
* p指针所指向的剩下的链表的第一个结点
* q指针的作用是在p指针所指向的结点插入到L链表后,防止剩下的链表丢失
* 依次将表中的p指向的元素值与L中最后一个数比较:如果不相等则按照尾插法插入;如果相等,p继续向后遍历
*/
void delete_re(Linklist &L){
Lnode *p=L->next,*q,*a=L;
L->next=NULL;
while (p){
q=p->next;
if (a==L || a->data!=p->data){
a->next=p;
a=p;
p->next=NULL;
}
p=q;
}
}
十三、假设有两个按元素值递增次序排列的线性表均以单链表形式存储。请编写算法将这两个单链表归并为一个按元素值递减次序排列的单链表。并要求利用原来两个单链表的结点存放归并后的单链表。
/*
* 假定两个单链表分别为A和B,都带有头结点
* 定义指针a指向A->next,b指向B->next
* 将A链表从头结点后断开
* 之后均从第一个结点起进行比较,将值小的结点头插法差入A链表中
* 比较结束后,可能会有一个链表还有剩余的结点没有进行归并,再将他们按照头插法将其插入A中即可
*/
void merge(Linklist &A,Linklist &B){
Lnode *a=A->next,*b=B->next,*p;
A->next=NULL;
while (a&&b){
if (a->data<=b->data){
p=a->next;
a->next=A->next;
A->next=a;
a=p;
} else{
p=b->next;
b->next=A->next;
A->next=b;
b=p;
}
}
while (b){
p=b->next;
b->next=A->next;
A->next=b;
b=p;
}
while (a){
p=a->next;
a->next=A->next;
A->next=b;
a=p;
}
}
十四、设A和B是两个单链表(带头结点),其中元素递增有序。设计一个算法从A和B中的公共元素产生单链表C,要求不破坏 A、B的结点。
/*
* ①、分别定义两个指针a和b
* 将A和B从第一个结点开始比较
* 如果a->data小于b->data,则b指针不遍历,a向后遍历
* 否则b指针向后遍历,a不遍历
* 当找到相等的元素后,立即停止while循环
* ②、之后对相等元素及其之后的元素操作
* 与①中的类似,去比较a->data和b->data
* 如果a->data小于b->data,申请一个新结点,赋值为a->data,b指针不遍历,a向后遍历
* 否则申请一个新结点,赋值为b->data,b指针向后遍历,a不遍历
* 再将新结点按照尾插法插入到带头结点的新链表C中
* ③、对于②的while循环完成后
* 可能有一个链表还没有遍历完,之后对没有遍历完的链表剩余结点遍历
*/
Linklist find_common(Linklist A,Linklist B){
Lnode *a=A->next,*b=B->next;
Linklist C=(Lnode*) malloc(sizeof (Lnode));
C->next=NULL;
Lnode *c=C;
while (a&&b){
if (a->data!=b->data){
if (a->data>b->data){
b=b->next;
} else{
a=a->next;
}
} else break;
}
while (a&&b){
Lnode *s=(Lnode*) malloc(sizeof (Lnode));
if (a->data>b->data){
s->data=b->data;
b=b->next;
} else{
s->data=a->data;
a=a->next;
}
s->next=c->next;
c->next=s;
c=s;
}
while (a){
Lnode *s=(Lnode*) malloc(sizeof (Lnode));
s->data=a->data;
s->next=c->next;
c->next=s;
c=s;
a=a->next;
}
while (b){
Lnode *s=(Lnode*) malloc(sizeof (Lnode));
s->data=b->data;
s->next=c->next;
c->next=s;
c=s;
b=b->next;
}
c->next=NULL;
return C;
}
十五、已知两个链表A和B分别表示两个集合,其元素递增排列。编制函数,求A与B的交集,并存放于 A 链表中。
/*
* 分别定义两个指针a=A->next和b=B->next
* 将A从头结点之后断开
* 将A的剩余的链表和B从第一个结点开始比较
* 如果a->data小于b->data,则b指针不遍历,a向后遍历
* 否则b指针向后遍历,a不遍历
* 每次找到相等的元素后,用malloc函数申请一个新结点,将其data赋值相等的值
* 再将新结点按照尾插法插入到链表A中
*/
void find_common_toA(Linklist &A,Linklist B){
Lnode *a=A->next,*b=B->next,*p;
A->next=NULL;
Lnode *c=A;
while (a&&b){
p=a->next;
if (a->data!=b->data){
if (a->data>b->data){
b=b->next;
} else{
a=a->next;
}
} else{
Lnode *s=(Lnode*) malloc(sizeof (Lnode));
s->data=a->data;
s->next=c->next;
c->next=s;
c=s;
a=a->next;
b=b->next;
}
}
c->next=NULL;
}
十六、两个整数序列A=a1,a2,a3,…,am和B=b1,b2,b3,…,bn已经存入两个单链表中,设计一个算法,判断序列B是否是序列 A 的连续子序列。
/*
*带头结点
* 定义指针a和b用于分别遍历A和B
* 依次进行比较a和b所指向的结点的数值
* 如果不相等,a继续向后遍历,b不向后遍历
* 如果相等,进入循环,a和b都向后遍历;如果后面的遍历中出现不相等的情况,那么b指回B->next,便于对B重新遍历;再重复上述操作
* 如果a遍历完了,而b没有遍历完,则说明B不是A的连续子序列
* 如果a没有遍历完,而b已经遍历完,就说明B是A的连续子序列
*/
bool continuous_seq(Linklist A,Linklist B) {
Lnode *a = A->next, *b;
while (a) {
b=B->next;
while (a&&b&&a->data==b->data){
a=a->next;
b=b->next;
}
if (b==NULL){
return true;
}
if (a==NULL) {
return false;
} else a=a->next;
}
return false;
}
十七、设计一个算法用于判断带头结点的循环双链表是否对称。
/*
* 定义一个头指针a,其尾指针为a->pre,判断a->data是否等于a->pre->data
* 如果相等,a继续向后遍历,尾指针继续向前遍历,每遍历一次,判断一次,如果不相等返回false,直到a等于尾指针或者a->next等于尾指针
*/
bool symmetric(DreLinklist L){
Drelnode *a=L->next,*r=L->pre;
while (a!=r && a->next!=r){
if (a->data==r->data){
a=a->next;
r=r->pre;
} else{
return false;
}
}
return true;
}
十八、有两个循环单链表,链表头指针分别为 h1 和 h2,编写一个函数将链表 h2 链接到链表h1 之后,要求链接后的链表仍保持循环链表形式。
/*
* 带头结点
* 先找到h1的尾指针
* 将h2的结点按照尾插法插入到h1中
*/
void connect(Linklist &h1,Linklist &h2){
Lnode *a=h1->next,*b=h2->next,*b1;
while (a->next!=h1){
a=a->next;
}
while (b!=h2){
b1=b->next;
b->next=a->next;
a->next=b;
a=b;
b=b1;
}
}
十九、设有一个带头结点的循环单链表,其结点值均为正整数。设计一个算法,反复找出单链表中结点值最小的结点并输出,然后将该结点从中删除,直到单链表空为止,再删除表头结点。
/*
* 每次用循环找出最小值,并输出再将结点free掉
*/
void delete_min19(Linklist &L){
while (L->next!=L){
Lnode *p=L,*q=p->next;
int min=q->data;
while (q->next!=L){
if (min>q->next->data){
min=q->next->data;
p=q;
q=q->next;
} else q=q->next;
}
printf("%d ",min);
q=p->next;
p->next=q->next;
free(q);
}
}
二十、设头指针为L的带有表头结点的非循环双向链表,其每个结点中除有 pred(前驱指针),data(数据)和 next(后继指针)域外,还有一个访问频度域Freq,在链表被启用前,其值均初始化为零。每当在链表中进行一次 Locate(L,x)运算时,令元素值为 x的结点中 freq 域的值增 1,并使此链表中结点保持接访问频度非增(递减)的顺序排列,同时最近访问的结点排在频度相同的结点前面,以便使频繁访问的结点总是靠近表头。试编写符合上述要求的 Locate(L,x)运算的算法,该运算为函数过程,返回找到结点的地址,类型为指针型。
//定义结构体
typedef struct dlnode1 {
int data;
struct dlnode1 *pre;
dlnode1 *next;
int freq;
}dlnode1,*DLinklist1 ;
//Locate函数
/*
* 先找到data为x的结点,将该结点的freq++
* 在L链表中找到freq大于等于这个结点freq的结点
* 进行前插
* 返回该结点的地址
*/
DLinklist1 Locate(DLinklist1 &L,int x){
dlnode1 *p=L->next,*pre=L;
while (p&&p->data!=x){
p=p->next;
pre=pre->next;
}
p->freq++;
dlnode1 *bpre=L,*a=p,*b=bpre->next;
while (b&&p->freq<b->freq){
bpre=bpre->next;
b=b->next;
}
if (p->next==NULL){
pre->next=p->next;
p->pre=bpre;
bpre->next=p;
p->next=b;
b->pre=p;
} else{
pre->next=p->next;
p->next->pre=pre;
p->next=b;
b->pre=p;
bpre->next=p;
p->pre=bpre;
}
return a;
}
二十一、单链表有环,是指单链表的最后一个结点的指针指向了链表中的某个结点(通常单链表的最后一个结点的指针域是空的)。试编写算法判断单链表是否存在环。
/*
* 定义一个fast指针和slow指针,每次fast比slow往后多遍历一个结点
* 如果fast和slow相等那么有环
* 如果不相等且fast为空,或者fast的下一个节点为空则没有环
*/
bool circle(Linklist L){
Lnode *fast=L,*slow=L;
while (fast&& fast->next){
if (fast==slow){
return true;
}
fast=fast->next->next;
slow=slow->next;
}
return false;
}
二十二、【2009 统考真题】已知一个带有头头结点的单链表。假设该链表只给出了头指针 list。在不改变链表的前提下,请设计一个尽可能高效的算法,查找链表中倒数第k个位置上的结点(k 为正整数)。若查找成功,算法输出该结点的 data 域的值,并返回 1;否则,只返回 0。
/*
* 定义指针q,找到第k个结点
* 再定义一个指针p,指向list—>next,两个指针同时向后遍历,当q->next为空时,停止查找,此时p指向的是倒数第k个结点
*/
int search_last_k(Linklist L,int k){
Lnode *q=L->next,*p;
int count=1;
while (count!=k){
if (q==NULL){
return 0;
}
q=q->next;
count++;
}
p=L;
while (q!=NULL){
q=q->next;
p=p->next;
}
printf("%d",p->data);
printf("\n");
return 1;
}
二十三、【2012 统考真题】假定采用带头结息的单链表保存单词,当两个单词有相同的后缀时可共享相同的后缀存储空间,例如,“loading”和“being”的存储映像如下图所示。
设 strl 和 str2 分别指向两个单词所在单链表的头结点,链表结点结构包括data、next。请设计一个时间上尽可能高效的算法,找出由 strl 和 str2 所指向两个链表共同后缀的起始位置(如图中字符 i 所在结点的位置 p)。
/*
* 算法思想:找公共节点,即值相同的结点,与第八题类似
*/
Linklist same(Linklist M,Linklist N){
Lnode *a=M->next,*b=N->next;
int m= find_Link_nu(M);
int n= find_Link_nu(N);
if (m>n){
int c=m-n;
for (int i = 0; i < c; ++i) {
a=a->next;
}
} else if (n>m){
int c=n-m;
for (int i = 0; i < c; ++i) {
b=b->next;
}
}
while (a!=b){
a=a->next;
b=b->next;
}
return a;
}
二十四、【2015 统考真题】用单链表保存m个整数,结点的结构包括data、link,且Idatal<=n(n 为正整数)。现要求设计一个时间复杂度尽可能高效的算法,对于链表中 data 的绝对值相等的结点,仅保留第一次出现的结点而删除其余绝对值相等的结点。例如,若给定的单链表 head如下:
/*
* 算法思想:类似键(键的思想在顺序表那一章使用过);
* 新建一个数组a[],长度为n+1,每个初始化为0,定义两个指针pre和p
* 判断p指向的结点的值的绝对值与a[]相对应中的值是否小于1,如果大于1则进行结点删除
*/
void delete_abs_same(Linklist &L,int n){
int *a;
a=(int*) malloc(sizeof (int)*(n+1));
for (int i = 0; i < n+1; ++i) {
a[i]=0;
}
Lnode *pre=L,*p=pre->next;
while (p){
if (a[abs(p->data)]==0){
a[abs(p->data)]=1;
pre=p;
p=p->next;
} else if (a[abs(p->data)]>=1){ //abs(p->data)是绝对值函数
pre->next=p->next;
p=pre->next;
}
}
}