题目如下:
Given a linked list, determine if it has a cycle in it.
Follow up:
Can you solve it without using extra space?
题目的要求是判断一个链表中是否存在环。这个题目我还是想了很久的,最开始的思路是判断随着指针节点移动,其必然会与开始节点即head节点重合,如果重合就说明这个链表是个圈。这个想法显然是考虑不周的,因为题目中问的是是否“包含”链表,而不是问如何验证这个链表是环形链表,所以这么做是有问题的。然后思路到这里就中断了很长时间,我就先回家压了压惊。晚上睡觉躺在床上的时候终于把问题想通了。我当时想到了钟表的指针,一个走得快一个走得慢,走得快的一定会在某个时刻追上走得慢的指针,这样两个指针看起来就重合了。所以说,这道问题中我们也可以设置两个指针一个走的快,一个走得慢,然后进行循环,如果走得快的指针和走得慢的指针在某一时刻能指向同一内存地址,就说明这个链表中存在一个环。如果链表中不存在环的话,说明可以将链表看做一条直线,走得快的链表是绝对可能与走的慢的链表指向重合的,而且会在一定时刻后指向null,说明已经到达了链表末尾,此时循环结束,程序直接返回false即可(返回false说明程序中不存在环)。思路我们终于明确了,下一步就是写代码了,注意一下边界条件的判定就好,已Accepted的代码如下所示:
<span style="font-size:14px;"> public boolean hasCycle(ListNode head) {
ListNode faster = head;
while(head!=null && faster!=null){
head = head.next;
if(faster.next!=null){
faster = faster.next.next;
}else{
return false;
}
if(head == faster){
return true;
}
}
return false;
}</span>
这道题也是有Editoral Solution的,我们现在就一起来看一下这道题还有什么其他解法。Editoral Solution给出的第一种解法是使用HashSet的,每经过一个节点就将这个节点加入到HashSet中去,如果发现该节点已经在HashSet中了,说明这个节点之前已经到达过了,所以链表中肯定存在环,此时返回true即可。实现代码如下所示:
<span style="font-size:14px;">public boolean hasCycle(ListNode head) {
Set<ListNode> nodesSeen = new HashSet<>();
while (head != null) {
if (nodesSeen.contains(head)) {
return true;
} else {
nodesSeen.add(head);
}
head = head.next;
}
return false;
}</span>
该题另一种方法也是通过不同速度来判断是否有环的思路,刚才已经介绍过了,就不再赘述了,代码如下:
<span style="font-size:14px;">public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) {
return false;
}
ListNode slow = head;
ListNode fast = head.next;
while (slow != fast) {
if (fast == null || fast.next == null) {
return false;
}
slow = slow.next;
fast = fast.next.next;
}
return true;
}</span>
最后当然要给出这个Editoral Solution的地址了,其中还有一些内容是关于时间复杂度的分析,也是很重要的,地址在这里:https://leetcode.com/articles/linked-list-cycle/
哈哈,那就愉快地结束了。