环形链表

258 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第7天,点击查看活动详情

如果一个链表中的某个节点可以连续跟踪其next指针再次到达,则此链表有环,如下图,就是一个入环节点为3,环长为4的环形链表。 环.png

1.判断单链表是否有环

给你一个链表的头节点 head ,判断链表中是否有环。如果链表中存在环 ,则返回 true 。 否则,返回 false 。

方案1

在我们遍历链表的时候,将遍历过的节点保存,并且每次都遍历都先与集合中的节点比较,如果遇到相同的,则证明此链表有环。

fun hasCycle(head: ListNode?): Boolean {
    val set = HashSet<ListNode>()
    var node = head
    while (node != null) {
        if (set.contains(node)) {
            return true
        } else {
            set.add(node)
            node = node.next
        }
    }
    return false
}

此方案只需要遍历一遍,所以时间复杂度为O(N),由于需要使用HashSet记录遍历过的节点,所以空间复杂度为O(N)

方案2

以前玩QQ飞车的时候,经常被第一名超一圈,所以反过来思考,如果两名玩家在一个单链表上赛车,跑得快的在起跑之后如果能还能遇到跑得慢的,那么这个链表有环,如果快的提前到达了终点,说明无环。

fun hasCycle(head: ListNode?): Boolean {
    var slow = head
    var fast = head
    while (fast?.next != null) {
        slow = slow?.next
        fast = fast.next?.next
        if (slow == fast) {
            return true
        }
    }
    return false
}

此方案只需要遍历一遍,所以时间复杂度为O(N),由于只使用了两个变量,所以空间复杂度为O(1)

2.求环形链表的入环节点

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

思考

我们假设起点到入环节点长度为S1,入环节点到第一次相遇长度为S2,环的剩余长度为S3,在上一题目中,当两名玩家相遇的时候,慢玩家slow跑得路程为S1+S2,快玩家fast跑得路程为S1+S2+S3+S2,由于快玩家fast的速度是慢玩家slow速度的2倍,所以可以得出快玩家fast跑过的路程也是慢玩家slow的2倍,即2*(S1+S2)=S1+S2+S3+S2,化简之后可以得到S1=S3。那么当两名玩家首次相遇之后,让其中一名玩家回到起点,然后两名玩家一起以相同的速度行驶,再次相遇的时候,相遇点就是入环节点。示意图如下: 入环.png

fun detectCycle(head: ListNode?): ListNode? {
    var slow = head
    var fast = head
    while (fast?.next != null) {
        slow = slow?.next
        fast = fast.next?.next
        if (slow == fast) {
            fast = head
            while (slow != fast) {
                slow = slow?.next
                fast = fast?.next
            }
            return slow
        }
    }
    return null
}

3.求环形链表的环长

给定一个链表的头节点  head ,返回链表的环长。 如果链表无环,则返回 null

思考

当慢玩家slow和快玩家fast相遇之后,让慢玩家slow继续前进并记录路程,当慢玩家slow再次回到之前的相遇节点的时候,这段路程就是环长。

fun getCycleLength(head: ListNode?): Int? {
    var slow = head
    var fast = head
    while (fast?.next != null) {
        slow = slow?.next
        fast = fast.next?.next
        if (slow == fast) {
            var length = 0
            var current = slow
            while (current != null) {
                current = current.next
                length++
                if (current == slow) {
                    return length
                }
            }
        }
    }
    return null
}