【为进大厂力扣刷题】3. 环形链表

171 阅读3分钟

“Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。”

一、题目描述:

141. 环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false

  image.png

示例 1:

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

示例 2:

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

示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

提示:

链表中节点的数目范围是 [0, 104]

-105 <= Node.val <= 105

pos 为 -1 或者链表中的一个 有效索引 。

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

二、思路分析:

链表通常最后一项的next指向 null,环形链表的意思就是最后一项next指向之前的某个节点,形成了一个环路。题目是让判断是否为环路,只要循环链表,判断next不是null就为环路,但是怎么知道又走到之前的节点呢。

最简单的侵入式改法,第一次循环加个tag,再回来判断属性是否存在就行,哈哈哈

解法1 侵入式

var hasCycle = function(head) {
    while(head){
        if(head._tag){
            return true
        }
        head._tag = true
        head = head.next
    }
    return false
};

image.png 时间复杂度O(n)

解法2 Set

判断走到之前节点,实质问题在查重,这时候想到 Set 是天然的去重器。用 Set.has() 判断感觉可以搞定,试一下

var hasCycle = function(head) {
    const SetBox = new Set()
    while(head) { // 如果不是环形链表,走到null就自动退出循环
        if(SetBox.has(head){
            return true
        } else {
            SetBox.add(head)
        }
        head = head.next
    }
    return false
}

image.png 这个执行用时稍微有点慢,时间复杂度O(n)

解法3 快慢指针

这个我没想到,学术名称快慢指针,原理是声明fast,slow两个指针都指向第0项,然后fast每次走两步,slow每次走一步,这样在完整循环一遍链表的时候,fast会追上slow。

怎么理解呢,写个公式就明白了

// step 代表步数, l代表链表长度
1step = 2step + (-l) // -l可以理解为快慢指针的差距。
step = l // 无论l等于几,步数就等于链表长度

或者理解为每走一步,快慢的差距就减少一

var hasCycle = function(head) { 
    let slow = head, 
    fast = head 
    while(fast && fast.next){ // 走得快,可能不是环形就为null了
        slow = slow.next 
        fast = fast.next.next 
        if(slow === fast){ 
            return true 
        } 
     } 
     return false 
};

image.png ok拉,顾白~