首先来看最基本的这个问题:如何判断一个单链表是否存在循环,链表数目未知。算法不能破坏链表。
有三种解决思路:
- 第一种方法,将所有的遍历过的节点用某个结构存储起来,然后每遍历一个节点,都在这个结构中查找是否遍历过,如果找到有重复,则说明该链表存在循环;如果直到遍历结束,则说明链表不存在循环。
python里可以使用dict或者set来实现,查找的时间复杂度为O(1),遍历为O(n),存储空间需要额外的O(n)。所以整个算法的时间复杂度为O(n),空间复杂度为O(n)。
// 链表结点类
class Node():
def __init__(self, item=None):
self.item = item // 数据域
self.next = None // 指针域
// 判断是否为环结构并且查找环结构的入口节点
def findbeginofloop(head):
// 默认环不存在,为False
loopExist = False
// 如果头节点就是空的,那肯定就不存在环结构
if head == None:
return "不是环结构"
s = set()
while head.next:
// 判断遍历的节点是否在set中
if id(head) in s:
// 返回环入口
print("存在环结构")
return head.item
s.add(id(head))
head = head.next
return "不是环结构"
if __name__ == "__main__":
// 构建环
node1 = Node(1)
node2 = Node(2)
node3 = Node(3)
node4 = Node(4)
node5 = Node(5)
node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node5
node5.next = node2
print(findbeginofloop(node1))
- 第二种方法,比较的特别,是使用反转指针的方法,每过一个节点就把该节点的指针反向。
当有环的时候,最后指针会定位到链表的头部,如果到最后,都没有再到头部,那说明链表不存在循环。
这个方法会破坏掉链表,所以如果要求是不能破坏链表的话,我们最后就还需要反转一下,再将链表恢复。
这个方法使用的空间复杂度为O(1),时间复杂度为O(n)。
// 链表结点类
class Node():
def __init__(self, item=None):
self.item = item // 数据域
self.next = None // 指针域
// 判断是否为环结构并且查找环结构的入口节点
def findbeginofloop(head):
// 如果头节点就是空的,那肯定就不存在环结构
if head == None:
return "不是环结构"
h = head // 保存链表头
last = None
// 反转链表,此时会破坏原链表,若存在环会无法还原,且无法找到环入口
while head.next:
if head.next == h:
return "存在环结构"
tmp = head.next
head.next = last
last, head = head, tmp
// 不是环的情况下,还原链表
head.next = last
last = None
while head.next:
tmp = head.next
head.next = last
last, head = head, tmp
return "不是环结构"
if __name__ == "__main__":
// 构建环
node1 = Node(1)
node2 = Node(2)
node3 = Node(3)
node4 = Node(4)
node5 = Node(5)
node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node5
node5.next = node2
print(findbeginofloop(node1))
- 第三种方法,就是快慢指针。
快指针pf每次移动2个节点,慢指针ps每次移动1个节点,如果快指针能够追上慢指针,那就说明其中有一个环,否则不存在环。
这个方法的时间复杂度为O(n),空间复杂度为O(1),实际使用两个指针。
// 链表结点类
class Node():
def __init__(self, item=None):
self.item = item // 数据域
self.next = None // 指针域
// 判断是否为环结构并且查找环结构的入口节点
def findbeginofloop(head):
// 将头节点赋予slowPtr
slowPtr = head
// 将头节点赋予fastPtr
fastPtr = head
// 默认环不存在,为False
loopExist = False
// 如果头节点就是空的,那肯定就不存在环结构
if head == None:
return "不是环结构"
// fastPtr的下一个节点和下下个节点都不为空
while fastPtr.next and fastPtr.next.next:
// slowPtr每次移动一个节点
slowPtr = slowPtr.next
// fastPtr每次移动两个节点
fastPtr = fastPtr.next.next
// 当fastPtr和slowPtr的节点相同时,也就是两个指针相遇了
if slowPtr == fastPtr:
loopExist = True
print("存在环结构")
break
// 寻找并返回环入口
if loopExist == True:
slowPtr = head
while slowPtr != fastPtr:
fastPtr = fastPtr.next
slowPtr = slowPtr.next
return slowPtr.item
return "不是环结构"
if __name__ == "__main__":
// 构建环
node1 = Node(1)
node2 = Node(2)
node3 = Node(3)
node4 = Node(4)
node5 = Node(5)
node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node5
node5.next = node2
print(findbeginofloop(node1))