[LeetCode] Linked List Cycle 与 Linked List Cycle II
2016-03-07 14:37
344 查看
前言
Linked List Cycle 与 Linked List Cycle II是LeetCode链表系列很经典的两道题,值得研究一下。题目
https://leetcode.com/problems/linked-list-cycle/https://leetcode.com/problems/linked-list-cycle-ii/
Linked List Cycle
Given a linked list, determine if it has a cycle in it.Can you solve it without using extra space?
分析
判断链表有没有环,最容易想到的方法是,用一个哈希表来记录每个元素是否被访问过,一旦出现某个元素被重复访问,说明存在环。空间复杂度 O(n),时间复杂度 O(N)。而最好的方法是时间复杂度 O(n),空间复杂度 O(1) 的。我们可以设置两个指针,一个快一个慢,快
的指针每次走两步,慢的指针每次走一步,如果快指针和慢指针相遇,则说明有环。
关于这种“快慢指针法”,还可参看LeetCode官方博客文章:http://articles.leetcode.com/detecting-loop-in-singly-linked-list
另外,对于这种解法的正确性,即“快慢指针若相遇链表保证有环”的正确性,这里引用一个这篇博客文章提到的证明方法:
如图,在任意时刻,p1和p2都在环上。由于p1每次向前1步,p2每次向前两步,用相对运动的观点(中学物理)来看,把p1看作静止,那么p2每次相对p1向前1步,二者在顺时针方向上的距离每经过一个时刻就减少1,直到变为0,也即二者恰好相遇。这样就证明了对于有环链表,二者也是必然在某一时刻相遇在某个节点上的。(除非这个环有无穷大,那么p2追上p1可能需要t=∞的时间)
代码
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: bool hasCycle(ListNode *head) { ListNode * SlowPtr = head, *FastPtr = head; while (FastPtr != NULL && FastPtr->next != NULL) { SlowPtr = SlowPtr->next; FastPtr = FastPtr->next->next;//快指针每次走两步 if (SlowPtr == FastPtr) return true; } return false; } };
Linked List Cycle II
Given a linked list, return the node where the cycle begins. If there is no cycle, return null.Note: Do not modify the linked list.
Follow up: Can you solve it without using extra space?
分析
题意很简单,如果有环,返回环的入口,如果没有,则返回NULL。对于有环的链表而言,当 fast 与 slow 相遇时, slow 肯定没有遍历完链表,而 fast 已经在环内循环了 n 圈 (1 ≤ n)。现在假
设 slow 走了 s 步,则 fast 走了 2s 步( 同时,fast 步数还等于 s 加上在环上多转的 n 圈),设环长为 r,则有:
2s = s + nr
因此,
s = nr
设整个链表长 L,环入口点与相遇点距离为 a,起点到环入口点的距离为 x,则
x + a = nr = (n–1)r + r = (n − 1)r + L − x
x = (n − 1)r + (L–x–a)
L–x–a 为相遇点到环入口点的距离,由此可知,从链表头到环入口点等于 n − 1 圈内环 + 相遇
点到环入口点,于是我们可以从 head 开始另设一个指针 slow2,两个慢指针每次前进一步,它俩一定会在环入口点相遇。
另外,刚才那篇文章最后也讨论了这个找环入口的问题,解释地相当清楚明了,一并推荐之。
代码
class Solution { public: ListNode *detectCycle(ListNode *head) { ListNode * SlowPtr = head, *FastPtr = head; while (FastPtr != NULL && FastPtr->next != NULL) { SlowPtr = SlowPtr->next; FastPtr = FastPtr->next->next; if (SlowPtr == FastPtr) {//存在环 ListNode *WalkerPtr = head;//重新设置一个慢指针 while (WalkerPtr != SlowPtr) { WalkerPtr = WalkerPtr->next; SlowPtr = SlowPtr->next;//直到两个慢指针相遇 } return WalkerPtr; } } return NULL; } };
相关文章推荐
- [C/C++]反转链表
- C#实现基于链表的内存记事本实例
- C语言实现带头结点的链表的创建、查找、插入、删除操作
- C++实现简单的学生管理系统
- Linux内核链表实现过程
- C++链表倒序实现方法
- C#通过链表实现队列的方法
- C#实现的简单链表类实例
- 找出链表倒数第n个节点元素的二个方法
- Java数据结构之简单链表的定义与实现方法示例
- C语言单循环链表的表示与实现实例详解
- C++实现的链表类实例
- PHP小教程之实现链表
- PHP中模拟链表和链表的基本操作示例
- C语言双向链表的表示与实现实例详解
- js链表操作(实例讲解)
- C语言实现输出链表中倒数第k个节点
- C++语言实现线性表之链表实例
- STL list链表的用法详细解析
- C语言创建链表错误之通过指针参数申请动态内存实例分析