文章目录
常考题:
题目一、反向输出带头结点的单链表全部结点的值
在之前的章节中已经提到过了单链表的定义为:
typedef struct LNode
{
int data;
LNode * next;
}LNode;
那么假定一个链表LL的逻辑结构为:
结点 | LL(头结点) | A | B | C | D |
---|---|---|---|---|---|
数据域 | 空 | 1 | 2 | 3 | 4 |
反向输出单链表,结果应该为:4,3,2,1
如何实现反向输出单链表呢?
第一种方法(推荐):
递归法,从首节点开始递归,递归到结点为NULL终止;
实现代码如下:
//反向输出链表的值
void ReversPrintList(LNode* pp)
{
if(pp==NULL)return ;
ReversPrintList(pp->next);
printf("%-3d",pp->data);
return ;
}
第二种方法
还有一种方法就是先将链表反转,然后在继续输出,此法用到文章(单链表的其他操作)的逆转函数void ReverseList(LNode *pp);然后输出即可;
题目二、找出带头结点的单链表倒数第k(k>0)个结点
结点 | LL(头结点) | A | B | C | D | E | F | G | H | NULL |
---|---|---|---|---|---|---|---|---|---|---|
数据域 | 空 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
在第一题的链表LL增加几个结点,假设需要找出倒数第二个结点,应该怎么操作?
倒数第二个结点为:结点G
第一种方法:
我们可以定义两个指针 p1和p2都指向链表头结点,
先让p1从头结点向后移动两个结点,p2不动,此时p1已经到了结点B,
然后再让p1和p2一起向后移动,当p1移动到NULL时,p2正好在倒数第二个结点,大家可以自己对照表格比划一下;
实现代码如下:
//找出带头结点的单链表倒数第k(k>0)个结点
//kk表示倒数第几个结点
LNode *FindKNode(LinkList LL,unsigned int kk)
{
if(LL==NULL){printf("链表不存在。\n");return NULL;}
if(kk<1){printf("请填入一个大于0的数\n");return NULL; }
LNode* p1=LL;
LNode* p2=LL;
int pos=0; //从头结点开始
//先将p1向后移动kk个位置
while(p1!=NULL && pos<kk)
{
p1=p1->next;
pos++;
}
//p1存在
if(p1==NULL || pos!=kk)return NULL;
//同时将p1和p2开始向后移动,直到p1移动到NULL,p2则为所寻找地址
while(p1!=NULL && p2!=NULL)
{
p1=p1->next;
p2=p2->next;
}
return p2;
}
第二种方法
还有一种方法就是利用文章
‘链式存储与单链表(上)’ 中的求链表长度函数:
int LengthList(LinkList LL)
和 文章’链式存储与单链表(下)'中的位序定位函数:
LNode* LocateNode(LinkList LL, unsigned int ii);
倒数第kk个元素的位序(位置) 实际上就是 链表的总长度 减去kk+1的 位序;
即 LocateNode(LL,LengthList(LL)-kk+1);就能得到倒数第kk个结点;
题目三、判断单链表是否有环,并且判断环的入口
目前此处该问题的思路我也不是很明白,所以如果有兄弟知道或者理解的话,还望在评论区不吝赐教
代码实现如下:
// 判断单链表是否有环,并找到环的入口。
LNode *FindLoop(LinkList LL)
{
if(LL==NULL){printf("链表不存在。\n"); return NULL;}
LNode *slow,*fast; //设置快慢指针, slow每移动一个结点,fast移动两个结点
//慢指针跑完一个圈,快指针能跑完两个圈,所以在慢指针跑完一个圈之前,快指针绝对会和慢指针相遇
slow=fast=LL; //同时从首结点开始向后移动
//找到快慢指针的相遇点(相遇结点的地址)
while( fast!=NULL && fast->next!=NULL ) //防止链表美环操作空指针的情况
{
slow=slow->next;
fast=fast->next->next;
if(slow==fast)break;
}
//如果存在fast为空 或者fast的下一个结点为空,说明链表没有构成环
if(fast==NULL || fast->next== NULL)return NULL;
//此时已经找到相遇结点,再定义两个指针
//一个指针从相遇结点开始跑,另外一个结点从头结点开始跑,他们就会相遇
LNode *p1,*p2;
p1=LL;
p2=fast;
while(p1!=p2)
{
p1=p1->next;
p2=p2->next;
}
return p2;
}
题目四、找出两个汇聚单链表的的公共结点
思路:假定链表La,Lb为两个汇聚的单链表;
链表结点 | La(头结点) | A | B | C | D | E | F | G | H | I |
---|---|---|---|---|---|---|---|---|---|---|
数据域 | 空 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
链表结点 | Lb(头结点) | R | S | T | G | H | I | |||
数据域 | 空 | 20 | 21 | 22 | 7 | 8 | 9 |
汇聚处结点为:E
那么我们如何从找到这个汇聚结点呢?
1.定义两个指针p1,p2; p1指向La,p2指向Lb;
2.分别查询出两个链表的长度,La长度为9,Lb链表长度为6,然后将p1指针向后移动(9-6)个结点,p1所指结点为C, p1指针所指部分链表长度为6,p2此时所指链表长度也为6,相等;
3.然后p1,p2开始向后一起移动,当p1==p2时,此时的地址即为汇聚结点;
代码实现如下:
// 找出两个汇聚单链表的公共结点,如果没有汇聚,返回空,如果有,返回汇聚的第一个结点。
// 找出两个汇聚单链表的公共结点,如果没有汇聚,返回空,如果有,返回汇聚的第一个结点。
LNode *FindJoin(LinkList La,LinkList Lb)
{
if(La==NULL || Lb==NULL){ printf("链表不存在。\n");return NULL; }
int Len_a,Len_b;
Len_a=LengthList(La);
Len_b=LengthList(Lb);
//如果是链表b长,就把Lb向后移动,直至两表长相等
for(;Len_b>Len_a;Len_b--)
{
Lb=Lb->next;
}
//如果是链表a长,就把La向后移动,直至两表长相等
for(;Len_a>Len_b;Len_a--)
{
La=La->next;
}
//此时Len_a==Len_b; 同时向后开始移动结点,如果汇聚了,必定会存在汇聚结点;
while(La!=NULL && La!=Lb )
{
La=La->next;
Lb=Lb->next;
}
if(La==Lb)return La;
return NULL;
}
题目五、假设线性表L=(a1,a2,a3…an-2,an-1,an)采用带头结点的单链表保存,请设计一个空间复杂度为O(1)且时间上尽可能高效的算法,重新排列L中的各结点,得到线性表L’=(a1,an,a2,an-1,a3,an-2.…)。
代码实现如下:
// 假设线性表L=(a1,a2,a3,...,an-2,an-1,an)采用带头结点的单链表保存,数据结点的个数为偶数。
// 请设计一个空间复杂度为O(1)且时间上尽可能高效的算法,重新排列L中的各结点。
// 最后得到线性表L=(a1,an,a2,an-1,a3,an-2,...)。
void ReplaceList(LinkList LL)
{
// 第1步:找到链表的中间结点。
LNode *fast=LL,*slow=LL; // 设置快慢指针。
while (fast->next != NULL)
{
// 慢指针走一步,快指针走两步。
slow=slow->next;
fast=fast->next->next; // 因为数据结点的个数为偶数,所以这里不会出现操作空指针的情况。
// printf("slow=%p,data=%d\n",slow,slow->data);
// printf("fast=%p,data=%d\n",fast,fast->data);
}
// slow指向链表前半段最后一个结点。
// printf("slow=%p,data=%d\n",slow,slow->data);
// 第2步:把链表的后半段原地逆置。
ReverseList(slow);
// PrintList(LL);
// 第3步:把链表的后半段插入到前半段中。
LNode *n1=LL->next; // n1为前半段第一个数据结点。
LNode *n2=slow->next; // n2为后半段第一个数据结点。
slow->next=NULL; // 链表结束标志。
LNode *tmp;
while (n2 != NULL)
{
tmp=n2->next; // 保存下一结点的地址。
n2->next=n1->next;
n1->next=n2;
n2=tmp;
n1=n1->next->next; // n1跳到下一个插入位置。
}
}