题目描述

判断给定的链表中是否有环。如果有环则返回true,否则返回false。
你能给出空间复杂度的解法么?
输入分为2部分,第一部分为链表,第二部分代表是否有环,然后回组成head头结点传入到函数里面。-1代表无环,其他的数字代表有环,这些参数解释仅仅是为了方便读者自测调试
示例1
输入:{3,2,0,-4},1
返回值:true
说明:第一部分{3,2,0,-4}代表一个链表,第二部分的1表示,-4到位置1,即-4->2存在一个链接,组成传入的head为一个带环的链表 ,返回true

题目分析

图片说明
有环的链表,在遍历时会在环中一直循环,直观地想,可以使用hash法保存出现的结点,在遍历过程时,碰到重复的结点即为有环。

解题思路

方法1:hash法,记录第一次重复的结点
通过使用set或者map来存储已经遍历过的结点,当出现重复的结点时,即为有环。

方法2:快慢指针
通过定义slow和fast指针,slow每走一步,fast走两步,若是有环,则一定会在环的某个结点处相遇(slow == fast),如下图分析。

代码实现

方法1:hash法,记录第一次重复的结点

public boolean hasCycle(ListNode head) {
        // 使用set来记录出现的结点
        HashSet<ListNode> set = new HashSet<>();
        while(head != null){
           // 当set中包含结点,说明第一次出现重复的结点,即环的入口结点
            if(set.contains(head)){
                return true;
            }
            // set中加入未重复的结点
            set.add(head);
            head = head.next;
        }
        return false;
    }

时间复杂度:O(n),需要将链表的所有结点遍历一遍,时间复杂度为O(n);
空间复杂度:O(n),需要额外的n空间的hashset来存储结点。

方法2:快慢指针

public boolean hasCycle(ListNode head) {
        if(head == null) return false;
        // 定义快慢指针
        ListNode slow = head;
        ListNode fast = head;
        while(fast != null && fast.next != null){
            // 快指针是满指针的两倍速度
            fast = fast.next.next;
            slow = slow.next;
            // 记录快慢指针第一次相遇的结点
            if(slow == fast) return true;
        }
        return false;
    }

时间复杂度:O(n),最多遍历链表的2*(X+Y)个结点,时间复杂度为O(n);
空间复杂度:O(1),需要额外两个快慢指针来遍历结点。