LeetCode:环形链表II

文章收录于LeetCode专栏
LeetCode地址


环形链表II

题目

  给定一个链表,返回链表开始入环的第一个节点。如果链表无环,则返回null。
  为了表示给定链表中的环,我们使用整数pos来表示链表尾连接到链表中的位置(索引从0开始)。如果pos是-1,则在该链表中没有环。
  注意,pos仅仅是用于标识环的情况,并不会作为参数传递到函数中。
  说明:不允许修改给定的链表。
  进阶:你是否可以使用 O(1) 空间解决此题?

  示例 1:
在这里插入图片描述

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

  示例 2:

在这里插入图片描述

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

  示例 3:

在这里插入图片描述

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

哈希表

算法思路

  借助Set数据结构不能存放重复数据的特性来判断链表是否有环,且记录当前节点,从而可在找到环路之后直接返回入环第一个节点。

编码

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
class Solution{
    public ListNode detectCycle(ListNode head) {
        if(head == null){
            return null;
        }
        Set<ListNode> set = new HashSet<>();
        ListNode temp = head;
        while(temp != null){
            if(!set.add(temp)){
                return temp;
            }
            temp = temp.next;
        }
        return null;
    }
}

复杂度分析

  因为整个算法仅使用了一层循环遍历,所以其时间复杂度为O(n)。但是使用了一个HastSet辅助判断是否有重复节点,所以空间复杂度为O(1)。

快慢指针

算法思路

  我们也可以借助LeetCode 141题所讲的快慢指针来解答这个道题,首先定义一个快指针(每次走两步),再定义一个慢指针(每次走一步)。如果链表中存在环,慢指针一定会在第一轮与快指针相遇,我们假设环形链表一共有a+b个节点,其中a表示入环口之前的a个节点,b表示形成环的b个节点。
  假设快指针和慢指针第一次相遇时,快指针走了f步,慢指针走了s步:

  • 因为快指针每次走的步数是慢指针的两部,所以有f=2s;
  • 如果快指针要想在环内与慢指针相遇,那么快指针就必须得比慢指针多走n个环的长度,即n*b(b为环内节点个数);

  又假设慢指针在环内走了c步后与快指针相遇,那么这里就有:f = a + nb + c,s = a + c。结合前序条件f=2s,综合三个式子可得:

f = a + c + nb
s = a + c
f = 2s
将s = a + c 和 f = 2s代入f = a + c + nb求得
2s = s + nb => s = nb
再将s = nb代入s = a + c求得
nb = a + c => a = nb - c

  最后求得a = nb - c说明从链表起点还是走a步(即入环口),刚好等于环内一圈减去从入环口到相遇点的步数。有了这个结论之后,我们就可以在快慢指针第一次相遇时,将快指针重新指向链表头每次向前走一步,同时慢指针继续按照每次一步向前移动,当他们再次相遇的点,就是入环后的第一个节点。
在这里插入图片描述

编码

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
 class Solution{
     public ListNode detectCycle(ListNode head){
         if(head == null){
             return null;
         }
         ListNode slow = head;
         ListNode fast = head;
         while(fast != null && fast.next != null){
             slow = slow.next;
             fast = fast.next.next;
             if(slow == fast){
                 fast = head;
                 while(fast != slow){
                     fast = fast.next;
                     slow = slow.next;
                 }
                 return fast;
             }
         }
         return null;
     }
 }

复杂度分析

  因为整个算法仅使用了一层循环遍历,所以其时间复杂度为O(n),并且没有使用任何额外内存空间所以空间复杂度为O(1)。


一键三连,让我的信心像气球一样膨胀!

相关推荐

  1. leetcode142.环形II

    2024-06-09 02:18:03       47 阅读
  2. LeetCode热题100】【环形 II

    2024-06-09 02:18:03       16 阅读
  3. LeetCode[141] [142] 环形I II

    2024-06-09 02:18:03       42 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-09 02:18:03       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-09 02:18:03       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-09 02:18:03       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-09 02:18:03       18 阅读

热门阅读

  1. Linux内核链表源代码

    2024-06-09 02:18:03       6 阅读
  2. IP路由基础&ospf

    2024-06-09 02:18:03       11 阅读
  3. 微信小程序的tabbar怎么配置

    2024-06-09 02:18:03       9 阅读
  4. zeppelin(kylin的可视化界面安装)(从头到尾安装)

    2024-06-09 02:18:03       11 阅读
  5. Hash & String 学习笔记

    2024-06-09 02:18:03       8 阅读
  6. creating an HTML file with SQL*Plus

    2024-06-09 02:18:03       9 阅读
  7. CVPR2024论文解读大盘点

    2024-06-09 02:18:03       9 阅读
  8. 新日本语教程 上册语法汇总

    2024-06-09 02:18:03       8 阅读