首先来看最基本的这个问题:如何判断一个单链表是否存在循环,链表数目未知。算法不能破坏链表。
有三种解决思路:

  1. 第一种方法,将所有的遍历过的节点用某个结构存储起来,然后每遍历一个节点,都在这个结构中查找是否遍历过,如果找到有重复,则说明该链表存在循环;如果直到遍历结束,则说明链表不存在循环。
    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))
  1. 第二种方法,比较的特别,是使用反转指针的方法,每过一个节点就把该节点的指针反向。
    当有环的时候,最后指针会定位到链表的头部,如果到最后,都没有再到头部,那说明链表不存在循环。
    这个方法会破坏掉链表,所以如果要求是不能破坏链表的话,我们最后就还需要反转一下,再将链表恢复。
    这个方法使用的空间复杂度为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))
  1. 第三种方法,就是快慢指针。
    快指针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))