当前位置: 首页 > news >正文

【LeetCode 热题 100】234. 回文链表——快慢指针+反转链表

Problem: 234. 回文链表
题目:给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

文章目录

  • 整体思路
  • 完整代码
  • 时空复杂度
    • 时间复杂度:O(N)
    • 空间复杂度:O(1)

整体思路

这段代码旨在解决一个经典的链表问题:回文链表 (Palindrome Linked List)。问题要求判断一个单链表是否是回文结构,即从前向后读和从后向前读的序列是否相同。例如 1 -> 2 -> 3 -> 2 -> 1 是回文链表。

该算法采用了一种非常高效且空间优化的 “快慢指针 + 反转后半部分 + 比较” 的三步走策略。它避免了使用额外数组存储所有节点,从而实现了 O(1) 的空间复杂度。

  1. 第一步:使用快慢指针找到链表的中点

    • 算法初始化两个指针 slowfast,都指向链表的头节点 head
    • 通过一个 while 循环,让 fast 指针每次移动两步,slow 指针每次移动一步。
    • 这个循环的终止条件是 fast 到达或越过链表的末尾。当循环结束时:
      • 如果链表长度为奇数,slow 指针会恰好停在链表的正中间节点。
      • 如果链表长度为偶数,slow 指针会停在后半部分链表的第一个节点
    • 无论哪种情况,slow 指针都为我们准确地划分出了链表的后半部分。
  2. 第二步:反转链表的后半部分

    • slow 指针指向的节点开始,就是链表的后半部分。
    • 算法调用一个辅助函数 reverseList(slow),将这后半部分的链表进行原地反转。
    • 反转后,会得到一个新的头节点 head2,它指向反转后的后半部分的链表。
  3. 第三步:比较前半部分和反转后的后半部分

    • 现在,我们有了两个“子链表”需要比较:
      • 原始链表的前半部分,从 head 开始。
      • 反转后的后半部分,从 head2 开始。
    • 通过一个 while 循环,同时遍历这两个子链表(循环条件是 head2 != null,因为后半部分可能比前半部分短一个节点,在奇数长度情况下)。
    • 在循环中,比较两个指针指向节点的值 head.valhead2.val
      • 如果任何一对节点的值不相等,说明链表不是回文结构,立即返回 false
      • 如果值相等,则将两个指针都向前移动一步。
    • 如果循环正常结束(即所有对应的节点值都相等),说明链表是回文的,返回 true

注意:这个算法会修改原始链表的结构(后半部分被反转)。如果题目要求不能修改原链表,则需要在比较完成后再将后半部分反转回来恢复原状。

完整代码

/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {/*** 判断一个单链表是否是回文链表。* @param head 链表的头节点* @return 如果是回文链表则返回 true,否则返回 false*/public boolean isPalindrome(ListNode head) {// 步骤 1: 使用快慢指针找到链表的中点或后半部分的起点ListNode slow = head;ListNode fast = head;// fast 每次走两步,slow 每次走一步while (fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;}// 循环结束后,slow 指向了后半部分的头节点// 步骤 2: 反转链表的后半部分// head2 是反转后链表的头节点ListNode head2 = reverseList(slow);// 步骤 3: 比较前半部分和反转后的后半部分// head 指向原始链表的头,head2 指向反转后半部分的头while (head2 != null) {// 如果对应节点的值不相等,则不是回文链表if (head.val != head2.val) {return false;}// 移动两个指针,继续比较下一对节点head = head.next;head2 = head2.next;}// 如果循环正常结束,说明所有对应节点都相等,是回文链表return true;}/*** 辅助函数:原地反转一个单链表(迭代法)。* @param head 要反转的链表的头节点* @return 反转后新链表的头节点*/public ListNode reverseList(ListNode head) {ListNode pre = null;ListNode cur = head;while (cur != null) {ListNode nxt = cur.next;cur.next = pre;pre = cur;cur = nxt;}return pre;} 
}

时空复杂度

时间复杂度:O(N)

算法主要包含三个步骤,我们分别分析其时间复杂度:

  1. 查找中点:快慢指针遍历链表,slow 指针走了 N/2 步。时间复杂度为 O(N)
  2. 反转后半部分reverseList 函数被调用在链表的后半部分,其长度约为 N/2。反转一个长度为 k 的链表需要 O(k) 的时间。因此,这部分的时间复杂度是 O(N)
  3. 比较两部分:最后的 while 循环遍历了链表的后半部分,长度约为 N/2。时间复杂度为 O(N)

综合分析
算法的总时间复杂度是 O(N) + O(N) + O(N) = O(N)

空间复杂度:O(1)

  1. 主要存储开销:该算法没有使用任何与输入规模 N 成比例的额外数据结构(如数组或哈希表)。
  2. 辅助变量
    • isPalindrome 方法中,只使用了 slow, fast, head2 等几个指针变量。
    • reverseList 方法中,也只使用了 pre, cur, nxt 等几个指针变量。
    • 这些变量的数量是固定的,与链表长度无关。

综合分析
算法所需的额外辅助空间是常数级别的。因此,其空间复杂度为 O(1)。这是一个空间效率最优的原地算法。

http://www.lryc.cn/news/582013.html

相关文章:

  • TypeScript 基础与类型系统详解:从入门到实践
  • TB62216FTG,TB62216FNG东芝BiCD集成电路硅单片,PWM斩波型电机驱动集成电路
  • 【Chrome】‘Good助手‘ 扩展程序使用介绍
  • 【操作系统】页面置换
  • OpenWebUI(2)源码学习-后端retrieval检索模块
  • vulnhub靶机渗透:PWNLAB: INIT
  • 海外短剧系统开发:PC端与H5端的全栈实践与深度解析
  • Java-66 深入浅出 分布式服务 Netty详解 EventLoop
  • [特殊字符] Excel 读取收件人 + Outlook 批量发送带附件邮件 —— Python 自动化实战
  • 嵌入式硬件中电容的基本原理与实现详解02
  • Excel 的多线程特性
  • 线程安全的单例模式与读者写者问题
  • WebRTC与RTMP
  • GPT5完全多模态架构拆解:实时视频生成如何颠覆内容创作
  • 什么是去中心化 AI?区块链驱动智能的初学者指南
  • 【C++指南】STL queue 完全解读(一):原理剖析与实战应用
  • 开源鸿蒙(OpenHarmony)桌面版全面解析:架构适配、设备支持与开发实战
  • Matlab自学笔记六十二:求解三角函数方程的通解周期解
  • 【JAVAFX】webview导入本地html并传入参数
  • 【论文笔记】World Models for Autonomous Driving: An Initial Survey
  • excel日志表介绍
  • C++学习笔记01(自学草稿)
  • 国民经济行业分类 GB/T 4754—2017 (PDF和exce版本)
  • 中电金信 :十问高质量数据集:金融大模型价值重塑有“据”可循
  • 【Unity笔记】Unity 粒子系统 Triggers 使用解析:监听粒子进入与离开区域并触发事件
  • maven 发布到中央仓库常用脚本-02
  • .NET9 实现 JSON 序列化和反序列化(Newtonsoft.Json System.Text.Json)性能测试
  • ArcGIS 水文分析升级:基于深度学习的流域洪水演进过程模拟
  • 3S技术+ArcGIS/ENVI全流程实战:水文、气象、灾害、生态、环境及卫生等领域应用
  • 语音交互新纪元:Hugging Face LeRobot如何让机器人真正“懂你”