目录
以下面这个链表为例子:

由于单向链表是不可往前回朔的,所以我们无法将从后往前对比。
我们可以将后面半段,先反转一次。

再通过两个指针进行对比,第二个指针是链表的中间节点,我们需要找到这个中间节点。

接下来附上两段代码,
寻找中间节点:
- struct ListNode* middleNode(struct ListNode* head)
- {
- struct ListNode *slow=head,*fast=head;
- while(fast!=NULL&&fast->next!=NULL)
- {
- fast=fast->next->next;
- slow=slow->next;
- }
- return slow;
- }
反转链表:
- struct ListNode* reverseList(struct ListNode* head){
- struct ListNode * prev,*cur,*next=NULL;
- prev=NULL;
- cur=head;
- next=NULL;
- while(cur)
- {
- next=cur->next;
- cur->next=prev;
- //迭代
- prev=cur;
- cur=next;
- }
- return prev;
- }
上面这两个代码实直接从前面的题目摘下来的,链表的题目的背景一般是一样的,因此是适用的。
答案如下:
- struct ListNode* middleNode(struct ListNode* head)
- {
- struct ListNode *slow=head,*fast=head;
- while(fast!=NULL&&fast->next!=NULL)
- {
- fast=fast->next->next;
- slow=slow->next;
- }
- return slow;
- }
-
-
- struct ListNode* reverseList(struct ListNode* head){
- struct ListNode * prev,*cur,*next=NULL;
- prev=NULL;
- cur=head;
- next=NULL;
- while(cur)
- {
- next=cur->next;
- cur->next=prev;
- //迭代
- prev=cur;
- cur=next;
- }
- return prev;
- }
- bool chkPalindrome(ListNode* A)
- {
- ListNode *p1=A;
- ListNode *mid=middleNode(A);
- ListNode *p2=reverseList(mid);
- while(p1&&p2)
- {
- if(p1->val!=p2->val)
- return false;
- p1=p1->next;
- p2=p2->next;
- }
- return true;
- }

原链表我们可以表达如一下形式

想要找到c1节点,我们只需要在b链表中查找a链表中第一个与b链表的共同节点。
- struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
- struct ListNode *pa=headA,* pb=headB;
- while(pb)
- {
- pa=headA;
- while(pa)
- {
- if(pb==pa)
- return pb;
- pa=pa->next;
- }
- pb=pb->next;
- }
- return NULL;
- }
我们现将这两个链表便利一遍,得到两个链表的长度,然后再将长的那个链表先走几步,这个步数为这两个链表的长度之差。

变成下面这样,这样再次将两个链表遍历,遇到的第一个相同节点就是答案。

- struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
- struct ListNode *pa=headA,*pb=headB,* cura=headA,* curb=headB;
- int lena=1,lenb=1;
- while(cura->next!=NULL)
- {
- lena++;
- cura=cura->next;
- }
- while(curb->next!=NULL)
- {
- lenb++;
- curb=curb->next;
- }
- if(cura!=curb)//要是两个链表的尾结点不相同的话说明不是相交链表
- return NULL;
- if(lenb>lena)
- {
- for(int i=1;i<=lenb-lena;i++)
- {
- pb=pb->next;
- }
- }
- else
- {
- for(int i=1;i<=lena-lenb;i++)
- {
- pa=pa->next;
- }
- }
- while(pa!=pb)
- {
- pa=pa->next;
- pb=pb->next;
- }
- return pa;
- }
这就是两个方法的差距:

题目中的环指的是如下图的一个节奏,这个链表在实际应用中是没什么用的,这里只是为了用来锻炼我们的思维。

设置快慢指针,要是原本走在前面的指针都能与慢指针碰面,就说明快指针陷入了循环。
这一题里我们指能将快指针速度设置为慢指针的二倍(后面会有讲解)。

- bool hasCycle(struct ListNode *head) {
- struct ListNode * fast=head,* slow=head;
- while(fast&&fast->next)
- {
- fast=fast->next->next;
- slow=slow->next;
- if(slow==fast)
- return true;
- }
- return false;
- }
fast一次走2步,slow一次走1步,fast会不会错过slow
fast一次走3步,slow一次走1步,fast会不会错过slow
fast一次走M步,slow一次走N步,fast会不会错过slow(M>N)
以下面这个链表为例,快慢指针都已经进入环中,且slow与fast相聚N个节点。

这里我们需要通过相对速度来理解这个问题。
fast一次走2步,slow一次走1步,他们的相对速度为1。

当相距距离为0时,就说明这两个指针相遇了,说明fast一次走2步,slow一次走1步这种情况是不会错过的。
fast一次走3步,slow一次走1步,他们的相对速度为2。
得到以下的情况:

我们可以看出,当N为偶数的时候才会相遇,当他们错过的时候,fast只会领先一个节点。

这时候他们相距C-1,C为环的总结点个数。所以当C-1为偶数时这种情况才不会错过

谁都不可能和谁在一起一辈子。人就是这样,必须去习惯失去。——《秒速五厘米》