数据结构学习:有关带头结点的单向链表的常见考题(c语言)
- 1.将链表中pp结点之后的结点原地逆置(反转)。
- 2.反向打印链表中全部的元素。
- 3.找出带头结点的单链表倒数第k(k>0)个结点。
- 4.判断单链表是否有环,并找到环的入口。
- 5.找出两个汇聚单链表的公共结点,如果没有汇聚,返回空,如果有,返回汇聚的第一个结点。
- 6.将线性表L=(a1,a2,a3,...,an-2,an-1,an)变成L=(a1,an,a2,an-1,a3,an-2,...)。数据结点的个数为偶数。
1.将链表中pp结点之后的结点原地逆置(反转)。
解题思路: 通过设置两个指针ss\ssnext指针,其中一个ss指针指向pp结点的下一个节点,将pp指针后面的结点置空,留下链表中在pp结点前的元素,再利用设置的另一指针ssnext指针保存ss指针下一结点的信息,接着将ss指向的结点插入pp结点的后面,ss指针接着再指向指向下一结点,直到ss指针为空。
void ReverseList(LNode *pp)
{
LNode *ss,*ssnext;
ss=pp->next;
pp->next=NULL;
while(ss!=NULL)
{
ssnext=ss->next;
ss->next=pp->next;
pp->next=ss;
ss=ssnext;
}
}
2.反向打印链表中全部的元素。
解题思路: 利用递归实现,当指针一直没有指向表尾时,便一直调用自身函数,直到指针为空返回,便从链表结尾从后往前执行输出语句。 时间复杂度为O(n)。
void PrintList1(LNode *pp)
{
if(pp == NULL) return;
PrintList1(pp->next);
printf("%3d",pp->data);
}
3.找出带头结点的单链表倒数第k(k>0)个结点。
解题思路: 可以设置两个指针,一个先出发设为fast指针,一个后出发设为slow指针,先让fast指针从表头移动到正数第k个结点,再设置另slow指针保持与先出发的指针一样的速度,向后移动,直到fast指针移动到表尾,这时slow移动的位置就是表长减去k的位置,也就是倒数第k个位置。
LNode *FindKNode(LinkList LL,unsigned int kk)
{
LNode *fast=LL;
int ii=0;
while((fast!=NULL)&&(ii<kk))
{
fast=fast->next;
ii++;
}
if(fast == NULL) return NULL;
LNode *slow=LL->next;
while(fast!=NULL)
{
slow=slow->next;
fast=fast->next;
}
return slow;
}
4.判断单链表是否有环,并找到环的入口。
解题思路: 思路一: 慢指针走一步,快指针走两步。快指针是慢指针速度的两倍,当快指针的路程是慢指针的两倍时,它们会相遇。 注意找环的入口的时候,需要通过计算:设从表头到环入口的距离为len,环的周长为peri,在慢指针进入环中的长度为x后与快指针相遇,快指针经过n圈环与慢指针相遇。 已知快指针的路程为两倍的慢指针的路程, 则可以得到: len+n* peri+x=2*(len+x)整理可得:len=n*peri-x,因此在快慢指针相遇后再设置一个指针从表头出发,保持与慢指针一样的速度 ,两个指针将会在环入口相遇。 图解:
LNode *FindLoop(LinkList LL)
{
LNode *fast,*slow;
fast=LL;
slow=LL;
while(fast!=NULL)
{
fast=fast->next->next;
slow=slow->next;
if(slow == fast) break;
}
if(fast == NULL) { printf("该链表没有环!\n"); return NULL; }
LNode *enter=LL;
while(slow!=enter)
{
slow=slow->next;
enter=enter->next;
}
return enter;
}
思路二: 还可以通过指针每走一步就遍历它所经过的结点,如果找到了相同的结点,那么就证明该链表具有环,且环的入口就是所找到的相同的结点。时间复杂度为O(n^2)
5.找出两个汇聚单链表的公共结点,如果没有汇聚,返回空,如果有,返回汇聚的第一个结点。
解题思路: 对于解决这道题,可以先求出两个链表长度之差n,再将较长链表的指针从头移动 n个结点,然后让较短链表的指针与较长链表的指针同时移动,直到找到汇聚点或者其中一个链表的表尾。时间复杂度为O(n)。
int LengthList(LinkList LL)
{
if (LL == NULL) { printf("链表LL不存在。\n"); return 0; }
LNode *pp=LL->next;
int length=0;
while (pp != NULL) { pp=pp->next; length++; }
return length;
}
LNode *FindJoin(LinkList La,LinkList Lb)
{
int lena,lenb,len;
lena=LengthList(La);
lenb=LengthList(Lb);
len=lena-lenb;
while(lena>lenb&&len>0)
{
La=La->next;
len--;
}
while(lenb>lena&&len<0)
{
Lb=Lb->next;
len++;
}
while(La!=Lb&&La!=NULL&&Lb!=NULL)
{
La=La->next;
Lb=Lb->next;
}
if(La == NULL||Lb == NULL) return NULL;
if(La == Lb) return La;
}
6.将线性表L=(a1,a2,a3,…,an-2,an-1,an)变成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)
{
if(LL == NULL) return;
LNode *fast,*slow;
fast=LL;
slow=LL;
while(fast->next!=NULL)
{
fast=fast->next->next;
slow=slow->next;
}
ReverseList(slow);
LNode *front,*back,*tmp;
front=LL->next;
back=slow->next;
slow->next=NULL;
while(back!=NULL)
{
tmp=back->next;
back->next=front->next;
front->next=back;
back=tmp;
front=front->next->next;
}
}
参考学习视频:B站数据结构学习视频
|