141. 环形链表

问题描述

给定一个链表,判断链表中是否有环。

为了表示给定链表中的环,我们使用整数 pos来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos-1,则在该链表中没有环。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

进阶:

你能用 O(1)(即,常量)内存解决此问题吗?

解决方案

快慢指针法

想象一下,有两个速度不同的跑步者。如果他们在直路上行驶,快跑者将首先到达目的地。但是,如果它们在圆形跑道上跑步,那么快跑者如果继续跑步就会追上慢跑者。

这正是我们在链表中使用两个速度不同的指针时会遇到的情况:

  1. 如果没有环,快指针将停在链表的末尾。
  2. 如果有环,快指针最终将与慢指针相遇。

所以剩下的问题是:

这两个指针的适当速度应该是多少?

一个安全的选择是每次移动慢指针一步,而移动快指针两步。每一次迭代,快速指针将额外移动一步。如果环的长度为 M,经过 M 次迭代后,快指针肯定会多绕环一周,并赶上慢指针。

时间复杂度:O(n)

空间复杂度:O(1)

show me the code

class Solution(object):
    def hasCycle(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """

        quick_pointer = slow_pointer = head             # 创建两个指针,分别记录一个快的遍历指针,和一个慢的便利指针

        while quick_pointer and quick_pointer.next:     # 当快指针为None,或快指针的下个一对象为None,说明不是环形链表
            slow_pointer = slow_pointer.next            # 慢指针前进一步
            quick_pointer = quick_pointer.next.next     # 快指针前进两步
            if slow_pointer == quick_pointer:           # 假如快指针等于慢指针,说明为环形链表
                return True
        return False

hash表存贮法

顺序遍历链表所有节点,若出现重复访问则说明有环,否则说明无环。这里注意不能用list保存访问过的节点,查找太慢了;用dict保存还要考虑到键不能是对象,所以这里采取以对象的id作为键的做法。

时间复杂度:O(n)

空间复杂度:O(n)

show me the code

class Solution2(object):
    def hasCycle(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        my_dict = {}                        # 创建一个字典来保存已经遍历过节点的内存地址
        while head and head.next:           # 当快指针为None,或快指针的下个一对象为None,说明不是环形链表
            if id(head) in my_dict:         # 假如当前节点在字典中则说明是环形链表
                return True
            else:
                my_dict[id(head)] = True    # 将当前节点加入到字典中

        return False

逆转链表检测法

倘若一个链表存在环,那么将这个链表反转,反转后的链表和原链表具有相同的head。证明起来比较麻烦,可以在纸上画一画来验证。

时间复杂度:O(n)

空间复杂度:O(n)

show me the code

class Solution3(object):
    def reverse_list(self, head):
        before = after = None
        while head:
            after = head.next # 保存当前节点的下一个节点
            head.next = before # 将当前节点的下一题个节点替换为当前节点的上一个节点
            before = head # 将上一个节点,往前移动,变为当前节点
            head = after # 当前节点向前移动
        return before # 返回反转完成后的头结点

    def hasCycle(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        if head and head.next and head == self.reverse_list(head): # 加入反转后的头结点与原先的头结点相同,则说明有环
            return True
        return False
posted @ 2019-04-07 22:09  YanceDev  阅读(1631)  评论(0编辑  收藏  举报