力扣142.环形链表 II

力扣142.环形链表 II

这道题是面试高频考点,也是 LeetCode Hot100 中的经典题目,我们先讲简单的哈希表解法,再重点分析空间复杂度 O (1) 的快慢指针最优解。

一、简单解法:哈希表(Set 容器)

核心思路是利用哈希表的 “唯一性” 记录遍历过的节点:

  • 遍历链表时,将每个节点的地址插入 C++ STL 的set<ListNode*>容器;
  • 若当前节点已存在于set中,说明该节点就是环的第一个节点;
  • 若遍历到链表末尾仍无重复节点,则链表无环。

该方法逻辑简单易懂,此处不再展开赘述,接下来重点讲解更优的快慢指针解法。

二、最优解法:快慢指针(空间复杂度 O (1))
1. 第一步:判断链表是否有环

利用 “一快一慢” 两个指针遍历链表,通过是否相遇判断是否存在环:

  • 快指针(fast):每次走 2 步;
  • 慢指针(low):每次走 1 步;
  • 若链表无环:fast 指针会先走到链表末尾(fast == nullptrfast->next == nullptr),两个指针永远不会相遇;
  • 若链表有环:fast 指针会在环内 “追上” low 指针,最终两者必定相遇(因为 fast 每次比 low 多走 1 步,相当于每次向 low 靠近 1 步)。

(无环)

(有环)

2. 第二步:找到环的第一个节点

当确认链表有环后,我们需要进一步定位环的入口,先定义几个关键长度:

  • a:链表头结点到环入口的距离;
  • b:环入口到快慢指针相遇点的距离;
  • c:相遇点回到环入口的距离。

推导:

  • 慢指针(low)走到相遇点时,总路程为:a + b
  • 快指针(fast)走到相遇点时,总路程为:a + b + n×(b + c)n为 fast 绕环的圈数);
  • 由于 fast 速度是 low 的 2 倍,因此路程也为 2 倍:2×(a + b) = a + b + n×(b + c)
  • 化简公式得:a + b = n×(b + c)
为什么 n=1?(fast 必在 low 入环第一圈追上)

很多同学会疑惑 fast 是否会绕多圈才追上 low,答案是不会

  • 当 low 刚进入环时,fast 已经在环内,且两者的距离小于等于环的总长度 L;
  • fast 每次比 low 多走 1 步,相当于 以 1 步 / 次的速度追上 low;
  • 最多需要 L 步就能追上,而 low 走 L 步刚好是环的一圈,因此 low 入环后第一圈内必被 fast 追上。

因此n=1,代入公式继续化简:a + b = b + ca = c(头结点到环入口的距离 = 相遇点到环入口的距离)。

3. 第三步:定位环的第一个节点

基于a = c的核心结论,定位环入口:

  1. 当快慢指针相遇后,将 fast 指针重新移回链表头结点;
  2. 让 fast 和 low 指针以相同速度(每次走 1 步) 同时前进;
  3. 当两者再次相遇时,所处的位置就是环的第一个节点。
算法复杂度
  • 时间复杂度:O (n)
  • 空间复杂度:O (1)
这道题算法题主要考察了两个知识点
  1. 快慢指针相遇 → 链表有环;
  2. 相遇后 fast 回头结点,两指针同速前进 → 相遇点即为环入口。
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode *detectCycle(ListNode *head) { if(head==NULL||head->next==NULL) return NULL; ListNode*fast =head; ListNode*low =head; while(fast!=NULL&&fast->next!=NULL) { fast=fast->next->next; low=low->next; if(fast==low) break;//fast和low相遇 } if(fast!=low) return NULL; fast=head; while(fast!=low) { fast=fast->next; low=low->next; } return fast; } };

Read more

Flutter 三方库 objectbox_generator — 自动化构建鸿蒙极速 NoSQL 数据库映射(适配鸿蒙 HarmonyOS Next ohos)

Flutter 三方库 objectbox_generator — 自动化构建鸿蒙极速 NoSQL 数据库映射(适配鸿蒙 HarmonyOS Next ohos)

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net。 Flutter 三方库 objectbox_generator — 自动化构建鸿蒙极速 NoSQL 数据库映射(适配鸿蒙 HarmonyOS Next ohos) 在高性能移动应用开发中,本地数据的持久化存储效率往往是决定用户感知流畅度的木桶短板。传统的 SQLite 虽然结构化程度高,但在处理大规模对象关系映射(ORM)时,复杂的 SQL 拼接和反射解析往往会成为性能瓶颈。 ObjectBox 作为一个专为移动设备打造的、跨平台的超高速 NoSQL 数据库,已经成为了许多追求极致体验开发者的首选。而在 Flutter for OpenHarmony 开发中,配合 objectbox_generator,我们可以通过注解驱动的自动化流程,掌握这套高性能数据库的核心用法。 ⚠️ 鸿蒙适配现状提示:截至本文撰写时,ObjectBox 的 Dart 插件尚未提供官方的 OpenHarmony

OpenClaw 配置 Nginx 反向代理完整指南

OpenClaw 配置 Nginx 反向代理完整指南

OpenClaw 配置 Nginx 反向代理完整指南 将 OpenClaw Gateway 安全地暴露到公网,并通过 HTTPS 和登录保护确保访问安全。 前言 OpenClaw 是一个强大的 AI 助手网关,默认情况下它只监听本地回环地址 (127.0.0.1:18789)。如果你想从外部网络访问 Control UI,或者为团队提供安全的访问入口,配置 Nginx 反向代理是最佳实践。 本文将介绍如何: * ✅ 配置 Nginx 反向代理到 OpenClaw * ✅ 启用 HTTPS (Let’s Encrypt SSL 证书) * ✅ 添加 Basic Auth 登录保护 * ✅ 配置 OpenClaw 信任代理模式 环境准备 * 服务器:

Flutter 组件 markup_analyzer 适配鸿蒙 HarmonyOS 实战:文本标签解析引擎,构建高性能动态排版与语义化渲染架构

Flutter 组件 markup_analyzer 适配鸿蒙 HarmonyOS 实战:文本标签解析引擎,构建高性能动态排版与语义化渲染架构

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 markup_analyzer 适配鸿蒙 HarmonyOS 实战:文本标签解析引擎,构建高性能动态排版与语义化渲染架构 前言 在鸿蒙(OpenHarmony)生态迈向深度内容分发、涉及富文本混排展示、自定义模板引擎或端侧跨端协议解析的背景下,如何将杂乱的标签数据高效转化为具备语义逻辑的 AST(抽象语法树),已成为决定应用排版性能与安全性的“逻辑枢纽”。在鸿蒙设备这类强调 AOT 极致执行速度与多维视窗适配的环境下,如果应用依然依赖基础的正则切割来处理动态标记,由于由于嵌套标签的递归复杂度,极易由于由于计算开销过大导致复杂长文阅读时的显著卡顿。 我们需要一种能够实现语法级扫描、支持自定义标签扩展且具备错误容错能力的解析引擎。 markup_analyzer 为 Flutter 开发者引入了轻量级且工业标准的标签分析方案。它通过词法扫描(Lexer)与语法构造,将零散的 Markup 字符串重组为具有层级关系的节点树。在适配到鸿蒙 H

Node.js 安装指南(Mac 版本)

Node.js 安装指南(Mac 版本)

目录 第一章 准备工作与环境检查 1.1 确认系统要求在开始安装 Node.js 之前,首先需要确认您的 Mac 系统是否符合要求: 1.2 检查现有 Node.js 安装 1.3 备份重要数据 1.4 清理可能的旧版本 第二章:安装方法概述与选择 2.1 主要安装方法比较 2.2 推荐安装方案 第三章:方法一 - 使用官方安装包 3.1 下载官方安装包 3.2 安装过程详解 3.3 验证安装 安装过程中遇到问题: 🧐 为什么会出现这个错误? ✅ 如何解决? 方案一:使用