翻译
给定一个链表,判断是否有一个循环在其中。
跟进:
你可以不用额外的空间来解决这个问题吗?
原文
Given a linked list, determine if it has a cycle in it.
Follow up:
Can you solve it without using extra space?
分析
解决方案 1 哈希表
有个效率不太高的方法,那就是在遍历这个链表的时候看看是否有某个节点曾近访问过。
这就用到了哈希的思想,不论是用map,还是set,甚至是vector,都是可以的。
下面的代码以set为例,如果visited中还没有包含这个节点,就将它添加到visited中。否则,就表示它已经访问过了,这也意味着这里存在一个循环。
bool hasCycle(ListNode *head) {
set<ListNode*> visited;
set<ListNode*>::iterator iter;
while (head != NULL) {
iter = visited.find(head);
if (iter == visited.end()) {
visited.insert(head);
} else {
return true;
}
head = head->next;
}
return false;
}
updated at 2016/09/20
public boolean hasCycle(ListNode head) {
HashSet<ListNode> visited = new HashSet<>();
while (head != null) {
if (visited.contains(head)) {
return true;
} else {
visited.add(head);
}
head = head.next;
}
return false;
}
哈希表方法的效率:
时间复杂度:O(n)
空间复杂度:O(n)
但是题目的有个更高的要求,那就是不用额外的空间,那么接下来我们就来看另一种方法。
解决方案 2 双指针
想象1,在一条直线的公路上,在速度保持稳定不变的情况下,有着更快的汽车和慢些的自行车,汽车在前,自行车在后,他们肯定是不可能的汇合的。
ListNode *bicycle = head;
ListNode *car = head->next;
while (bicycle != car) {
// other code
}
之所以要一前一后,只是为了排除一开始就是汇合的情况。当然,如果一开始是同一起点,那就要在开始while循环之前,先让他们动起来。
ListNode *bicycle = head;
ListNode *car = head;
bicycle = bicycle->next;
car = car->next->next;
while (bicycle != car) {
// other code
}
想象2,如果是在一个包含循环的公路上,也就是一直往前行驶着,但终究会回到之前走过的某个路段上。至于在这种公路上,他们为什么会汇合。我们在汇合的那一刻,把时间禁止,再回退。假设此时自行车离刚才设定的汇合点距离是S,汽车离该汇合点距离是2S,但自行车行驶这距离S的路段需要10分钟,而汽车行驶这距离2S的路段也是10分钟,那么10分钟他们不就相遇了吗?(别问我为什么汽车的速度只比自行车快1倍……
ListNode *bicycle = head;
ListNode *car = head->next;
while (bicycle != car) {
if (car == NULL || car->next == NULL)
return false;
bicycle = bicycle->next;
car = car->next->next;
}
这个双指针法的效率:
空间复杂度:
如果没有循环,那么结束的条件是汽车走完全程,这就取决于路程的长短了。所以是O(n)。
如果有循环,需要分开来看:设非循环的长度为N,循环的长度为M,非循环的路程显然都只用走一次,循环的路程可能要走K次。因此也就是O(N) + O(kM),虽然并不能知道k是多少,但走的总路程肯定还是可以用tn来计。t为常亮,n为N和M之和。所以时间复杂度仍然是O(n)。
空间复杂度:
这个就很明显的,就是O(1),原文只使用了2个指针节点,也就是个数为常量。
代码
C Plus Plus
bool hasCycle(ListNode *head) {
if (head == NULL || head->next == NULL)
return false;
ListNode *bicycle = head;
ListNode *car = head->next;
while (bicycle != car) {
if (car == NULL || car->next == NULL)
return false;
bicycle = bicycle->next;
car = car->next->next;
}
return true;
}
Java
update 2016/09/20
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null)
return false;
ListNode bicycle = head;
ListNode car = head.next;
while (bicycle != car) {
if (car == null || car.next == null)
return false;
bicycle = bicycle.next;
car = car.next.next;
}
return true;
}