JS-单链表是否有环以及环的入点问题

版权声明:本文为博主原创文章,未经博主允许不得转载,没关注点个关注哦 https://blog.csdn.net/qq_37288477/article/details/84702681

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

进阶:

你能否不使用额外空间解决此题?

方案1 哈希表

在这里插入图片描述

哈希表是最容易理解的一个方案
建立一个哈希表,如果不存在就向哈希表中添加数据,存在的话就直接返回true(存在的可能只有P点,同时P点也是环的入点(这个和下一道题有关))
缺点:占用大量的空间,实际中,一个链表中的数据是很多的,这时候你建立一个哈希表,就会重新建一个存在大量数据的额外空间。代码是给电脑看的,人虽然容易理解,但是电脑不在乎容易不容易,它在乎的时间和空间的使用情况,所以这个方法不是最佳的。时间复杂度虽然是O(n),同样空间复杂度也是O(n)

function hasCycle(head) {
    let nodesSeen = new Set()
    while (head != null) {
        if (nodesSeen.has(head)) {
            return true;
        } else {
            nodesSeen.add(head);
        }
        head = head.next;
    }
    return false;
}
方案2 快慢指针
建立两个指针fast和slow
每一次循环fast移动两个位置
fast=fast.next.next
slow移动一个位置
slow=slow.next
当slow==fast的时候,就说明这个链表中存在环,不要问为什么?自己动手画个图,按着代码走一遍就知道,看是看不懂得,我就不说明了
优点:他的时间复杂度也是O(n)和上面的哈希表是一样的,但是它的空间复杂度是O(1)的,远远小于哈希表的O(n),虽然但是人不容易理解,但是机器是不在乎的,你的代码是让机器运行的。
var hasCycle = function(head) {
    if (head == null || head.next == null) {
        return false
    }

    let fast = head.next
    let slow = head
    while (fast != slow) {

        if (fast == null || fast.next == null) {
            return false;
        }
        fast = fast.next.next
        slow = slow.next

    }
    return true
};

方案3 在链表中增加一个域visited
在链表中增加一个域visited(可以是a,b,c,d,…),初始化都为0,从链表的头部开始走,每走过一个链表就标记visited为1,如果要访问的下一个节点的visited域为1,那么证明链表中有环。
上面的快慢指针和哈希表都是需要额外的空间复杂度,进阶不需要额外的空间复杂度,这里就实现一下
var hasCycle = function(head) {
   
      while (head) {
        if (head.visited) return true
        head.visited = true
        head = head.next
    }
    return false
};

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

说明:不允许修改给定的链表。

进阶:

你是否可以不用额外空间解决此题?

方案1 哈希表
这个就是判断是否有环,有环的地方就是环的节点P 同样他的缺点是需要大量的额外空间复杂度,优点就是人容易理解(没什么卵用,代码让机器运行)
var detectCycle = function(head) {
    if (head == null || head.next == null) {
        return null
    }

    let hasObj = new Set()
    while (head != null) {
        if (hasObj.has(head)) {
            return head
        } else {
            hasObj.add(head)
        }
        head = head.next
    }

};
方案2 快慢指针
这里前半部分和上面判断是否有环一样,当有环的时候,fast==slow。这时候fast返回头结点head
fast=head
slow指针不变在原地
然后fast和slow指针每次移动一个位置,当fast==slow的时候,他们就在环的入口位置。不要问我为什么,自己走一遍,网上的图你看也看不懂。
fast=fast.next
slow=slow.next

在这里插入图片描述

优点还是那样:只需要O(1)的额外的空间复杂度
方法3 在链表中增加一个域visited
道理同上面的那个方案,只是返回值不同。其实这种方法和哈希表一样,只是他们的是在原链表中添加了一个点而已
var detectCycle = function (head) {
    while (head) {
        if (head.visited) return head
        head.visited = true
        head = head.next
    }
    return null

};

猜你喜欢

转载自blog.csdn.net/qq_37288477/article/details/84702681