题目描述:有一个单向链表,链表中有可能出现“环”,如下图,那么,如何用程序来判断该链表是否为有环链表呢?
方法一:
从头节点开始遍历每一个节点,每遍历一个新节点就从头检查到新节点之前的所有节点,如果和该新节点的值有重复,则证明该节点被遍历过两次,链表有环;如果不存在与新节点相同值的节点,就继续遍历下一个新节点,继续重复上述操作。
假设链表的节点数量为n,则该解法时间复杂度为O(),由于未创建额外存储空间,空间复杂度为O(1)。
方法二:
引入哈希表,创建一个以节点ID为key的set集合,用来存储曾经遍历过的节点。然后同样从头遍历单链表中的每一个节点,每遍历一个节点,都用新节点与set集合中存储的节点进行比较,如果发现set中存在与之相同的节点ID,则说明链表有环,如果不存在,就把这个节点ID存入set集合中,继续遍历下一个节点,重复上述操作。
两种方法本质区别是使用了set作为额外的缓存。
假设链表节点数量为n,则该解法的时间复杂度为O(n),由于创建了额外存储空间,空间复杂度为O(n)。
方法三:
模拟追及问题,首先创建两个指针p1和p2(在Python中就是两个对象引用),让它们同时指向链表的头节点,然后开始循环遍历节点,其中p1每次向后移动1个节点,p2每次向后移动2个节点,然后比较两个指针指向的节点是否相同,如果相同,则可以判断出链表有环,如果不同,则继续下一次循环。
假设链表节点数量为n,则该解法的时间复杂度为O(n),由于未创建额外存储空间,空间复杂度为O(1)。
Python代码实现如下:
class Node:
def __init__(self, data):
self.data = data
self.next = None
def is_cycle(head):
p1 = head
p2 = head
while p2 is not None and p2.next is not None:
p1 = p1.next
p2 = p2.next.next
if p1 == p2:
return True
return False
node1 = Node(5)
node2 = Node(3)
node3 = Node(7)
node4 = Node(2)
node5 = Node(6)
node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node5
node5.next = node2
print(is_cycle(node1))
扩展问题:如果链表有环,如何求出环的长度以及入环节点?
求环长:当两个指针首次相遇,证明链表有环后,两指针继续循环前进,并统计前进的循环次数,直到两个指针第2次相遇。此时,统计的前进次数就是环长。
求入环节点:假设从链表头节点到入环点距离是D,入环点到首次相遇点距离是S1,首次相遇点到入环点为S2。
由结论可得,把其中一个指针放回头节点的位置,另一个指针保持在首次相遇点,两个指针都是每次向前走1步。那么它们最终相遇的节点,就是入环节点。
本文参考整理自《漫画算法:小灰的算法之旅》