上次讲了如何实现判断单链表中是否存在回文
这次我们将算法提升一下,判断在哪里将会产生回文。如果没有链表中无回路则直接返回NULL
既然是算法提升,说明之前的算法仍有它的可取之处,分析一下,我们可以将此任务分为两步来解决:
①判断链表是否有回文,如果没有则直接返回NULL;
②如果有回文,如何利用数学分析来找到回文开始结点并返回该结点。
该题选自LeetCode 142,有兴趣的朋友可以直接去看看。
思路分析
首先我们仍然采用追击算法的思想,即当两个指针在回文里循环时,如果发生追击现象,则说明链表内有回文,否则没有。
那么,当我们得知有回文后,肯定还需要一些信息才能对回文开始的地方进行判断,那么这些信息在哪里获取呢?想一想,是否他们相遇的位置中存在其他隐含信息?
其实,他们相遇的位置确实隐藏着重要的信息,我们现在假设该链表分为两部分,前一部分是无回文的直链,后一部分是回文,两者相交的位置即我们最后要计算的位置,取名为P点
假设前一部分的长度为a,后一部分长为b
- 当fast_p(快指针)跑了一个a的距离时,slow_p(慢指针)跑了a/2。此时fast_p恰好在P点
- 当slow_p跑了一个a的时候,fast_p已经跑了2a的距离,假设回文单圈长度大于a,那么我们此时将回文总长划分为a+c(a+c=b),此时slow_p恰好在P点,而fast_p已经跑了一个a,他还剩一个c就跑完了一圈
- 当slow再跑了一个c之后,fast跑了2c,他(fast_p)此时恰好跑满了一圈、又多了一个c的长度,还差一个a的距离就又到P点;而slow此时刚刚第一次进去圈中,跑了一个c的长度,也差一个a的长度就到P点。(等等!他们此时距离P点的距离是一样的,那不就是说,他们此时已经相遇了吗? fast_p刚好在P点后距离P点c的地方追上了slow_p,仔细想想看是不是这样。)
- 我们再观察,此时fast_p和slow_p都离P点差一个a的距离,那么a又是多远的?诶!a不就是从head(头结点)开始到P点的距离吗?也就是说,如果现在再有一个slow_p2,让他从头开始跑,并且速度和slow_p是一样的,那么他们将恰好可以在P点相遇! 这不就刚好凑出了我们要的控制条件吗?
OK,综上所述,我们终于分析清楚了整个流程,编码就很简单了:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(head == NULL)
return NULL;
ListNode * fast_p = head;
ListNode * slow_p = head;
ListNode * p2 = NULL;
while(fast_p !=NULL && fast_p->next != NULL){
fast_p = fast_p->next->next;
slow_p = slow_p->next;
if(fast_p == slow_p){
p2 = fast_p;
while(head != p2){
head = head->next;
p2 = p2->next;
}
return head;
}
}
return NULL;
}
};