1 问题描述
给定一个链表,判断链表中是否有环,如果有则返回True,否则返回False。
2 链表拆解法
采用跨节点遍历的方法判断,这样不需要使用额外空间,只需要保存一个终止判断信号(也可以不需要,直接判断)。
首先明确什么情况链表没有环:(1)链表是空的;(2)链表遍历到最后的next是None。
基于此,我们可以考虑跨节点遍历的思想,将链表的 n n n个节点记为标号为 1 , . . . , i . . . , n 1,...,i...,n 1,...,i...,n,对于第 i i i个节点,我们令 L i . n e x t = L i + 1 . n e x t = L i . n e x t . n e x t L_i.next=L_{i+1}.next=L_i.next.next Li.next=Li+1.next=Li.next.next,再令 L i = L i . n e x t L_i=L_i.next Li=Li.next,这样,就跳过了一个节点,到达了第 i + 1 i+1 i+1个节点处,假如链表无环,那在遍历到最后时,可能出现 L i = N o n e , L i . n e x t = N o n e L_i=None,L_i.next=None Li=None,Li.next=None两种情况,出现这两种情况之一,链表都不存在环,否则,链表最终将会变成 L i = L i . n e x t . n e x t L_i=L_i.next.next Li=Li.next.next的情况,也就是链表中最后只有两个节点,这两个节点构成了一个环,因此,假如存在 L i = L i . n e x t . n e x t L_i=L_i.next.next Li=Li.next.next,那这个链表就是有环链表,以此完成对链表是否有环的判断。
python代码实现如下:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def hasCycle(self, head: ListNode) -> bool:
if (head is None) or (head.next is None):
return False
else:
signal = False
while signal is False:
head.next = head.next.next # 与下一行的顺序不可以调换
head = head.next
if head is None:
signal = True
return False
elif head.next is None:
signal = True
return False
elif head == head.next.next:
signal = True
return True
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)。
看了官方题解以后,发现存在一个问题,就是这个算法最终会破坏掉这个链表结构,虽然数据还在,但是无法访问了。
3 快慢指针法
这个应该是判断循环链表很经典的方法,通过快慢指针来进行判断,假如有环,那快慢指针一定会在某个时刻相等,否则就无环。
python代码实现:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def hasCycle(self, head: ListNode) -> bool:
if (head is None) or (head.next is None):
return False
else:
slow = head
fast = head.next
signal = False
while signal is False:
if slow == fast:
signal = True
return True
elif fast is None:
signal = True
return False
elif fast.next is None:
signal = True
return False
slow = slow.next
fast = fast.next.next
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)。
这个方法不会破坏链表。