判断一个链表是否有环,如果有,则找到环的入口点
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)