判断一个链表是否有环,如果有,则找到环的入口点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Node: def __init__(self, value): self.value = value self.next = None
def __repr__(self): retrun '%s' % self.value
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
|
要求
- 不允许修改链表结构
- 时间复杂度O(n),空间复杂度O(1)
思考
集合
创建一个存储节点 ID 为的集合,来存储曾经遍历过的节点.从头节点开始,依次遍历单链表的每一个节点.每遍历到一个新节点,就用新节点和集合当中存储的节点作比较,如果发现集合中存在相同节点 ID,则说明链表有环;否则存入新的节点.
1 2 3 4 5 6 7 8
| def whether_has_ring(node): node_set = set() while node: if node in node_set: return node node_set.add(node) node = node.next return None
|
或利用集合没有重复元素的特性,通过判断集合长度来判断链表是否具有环型结构
1 2 3 4 5 6 7 8 9 10
| def whether_has_ring(node): node_set = set() n = 0 while node: node_set.add(node) n += 1 if len(node_set) != n: return node node = node.next return None
|
快慢指针
使用 slow,fast 两个指针对链表进行遍历.slow 指针一次遍历 1个节点,fast 指针一次遍历 2 个节点.当快慢指针相交时,则表示链表有环.
若有环,则使用一个指针指向链表头节点,一个指针指向第一次的交汇点,一起向后遍历,当两个指针相等时,此时相交的位置即为环的入口点.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| def whether_has_ring(node): slow = node fast = node while fast.next and fast.next.next: slow = slow.next fast = fast.next.next if slow == fast: slow = node while slow.next and fast.next: slow = slow.next fast = fast.next if slow == fast: return slow return None
|
判断两个单链表是否相交
集合
如果两个链表相交,那么它们一定会有公共的结点.
创建一个存储节点 ID 为的集合,来存储链表 A 中节点.从链表 B 头节点开始,依次遍历单链表的每一个节点.每遍历到一个新节点,就用新节点和集合当中存储的节点作比较,如果发现集合中存在相同节点 ID,则说明链表相交;
1 2 3 4 5 6 7 8 9 10 11 12
| def whether_intersect(node1, node2): node_set = set() node = node1 while node: node_set.add(node) node = node.next node = node2 while node: if node in node_set: return node node = node.next return None
|
- 时间复杂度: O(m + n) = O(n)
- 空间复杂度: O(n)
尾节点法
如果两个链表相交,那么它们的最后一个节点一定相等.
首先判断两个链表尾节点是否相等,并获得两个链表的长度,l1,l2.若相等,使长链表先遍历 l1-l2 的绝对值长度.然后长短链表依次遍历.节点相等则为链表相交的位置.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| def whether_intersect2(node1, node2): l1 = 1 l2 = 1 node_a = node1 node_b = node2
while node_a.next: node_a = node_a.next l1 += 1 while node_b.next: node_b = node_b.next l2 += 1 if node_a == node_b: node_a = node1 node_b = node2 if l1 > l2: for i in range(l1 - l2): node_a = node_a.next if l1 < l2: for i in range(l2 - l1): node_b = node_b.next while node_a and node_b: if node_a == node_b: return node_a node_a = node_a.next node_b = node_b.next return None
|
- 时间复杂度: O(2(m+n)) = O(n)
- 空间复杂度: O(1)
利用有环链表
将其中一个链表首位相接,若相交,则剩下的结构为有环链表.利用判断是否是有环链表及获取有环链表的入口点即可解决问题
1 2 3 4 5 6 7 8 9
| def whether_intersect3(node1, node2): node_a = node1 while node1.next: node1 = node1.next node1.next = node_a
return whether_has_ring(node2)
|
- 时间复杂度: O(2(m+n)) = O(n)
- 空间复杂度: O(1) 或 O(n)