跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava算法

力扣 234. 回文链表

介绍力扣 234 题回文链表的两种解法。第一种方法是将链表转换为数组,利用双指针判断对称性;第二种方法是使用快慢指针定位中点,反转后半段链表后与前半段逐一比较。文章提供了详细的步骤解析与完整的 Java 代码实现,涵盖边界条件处理。

时间旅人发布于 2026/3/30更新于 2026/9/1086 浏览

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

示例 1: 输入:head = [1,2,2,1] 输出:true

示例 2: 输入:head = [1,2] 输出:false

**提示:**链表中节点数目在范围 [1, 10^5] 内,0 <= Node.val <= 9

题目解读

回文链表:本质是单链表的一种特殊结构 —— 从链表头部到尾部遍历得到的节点值序列,和从尾部到头部遍历得到的序列完全一致,比如 "abba"、"12321",正读和反读都相同。我们往往使用这'对称性'解决问题。

思路一

将链表转换为数组后使用双指针判断是否是回文。

具体步骤与解析

1. 统计链表长度
int len = 0; // 用以储存链表长度
ListNode cur = head; // 从头节点出发
while (cur != null) {
    len++; // 每遍历一个节点,长度 +1
    cur = cur.next; // 游标后移
}

作用:确定后续数组的长度,避免数组越界或浪费空间。

2. 将链表值存入数组
cur = head; // 重置游标,回到链表头
int[] res = new int[len]; // 根据长度创建数组
for (int i = 0; i < res.length; i++) {
    res[i] = cur.val; // 存入当前节点值
    cur = cur.next; // 游标后移
}

注意:这里 for 循环的终止条件是 i < res.length,和链表长度完全匹配,不会遗漏或越界。

3. 双指针判断回文
for (int i = 0, j = len - 1; i < j; i++, j--) {
    if (res[i] != res[j]) { // 对称位置值不等,直接返回 false
        return false;
    }
}
return true; // 所有对称位置相等,是回文

左指针 i 从数组头部(i=0)开始,右指针 j 从数组尾部(j=len-1)开始,只要有一对值不相等,直接返回 false;反之若循环结束(i >= j)都未发现不相等,返回 true。

核心思路总结

先统计长度→再存值到数组→双指针判回文。

完整代码
class Solution {
    public boolean isPalindrome(ListNode head) {
        int len = 0;
        ListNode cur = head;
        while (cur != null) {
            len++;
            cur = cur.next;
        }
        cur = head;
        int[] res = new int[len];
        for (int i = 0; i < res.length; i++) {
            res[i] = cur.val;
            cur = cur.next;
        }
        for (int i = 0, j = len - 1; i < j; i++, j--) {
            if (res[i] != res[j]) {
                return false;
            }
        }
        return true;
    }
}

思路二

先使用快慢指针找到链表中点将链表分为两个部分,再将后部分链表反转与前部分一一对应来判断回文链表。

具体步骤与解析

1. 处理边界情况
if (head == null || head.next == null) return true;

空链表 或 只有 1 个节点的链表,本身就是回文,直接返回 true。

2. 快慢指针找链表中点
while (fast != null && fast.next != null) {
    pre = slow; // 记录 slow 的前一个节点(分割点)
    slow = slow.next; // 慢指针走 1 步
    fast = fast.next.next; // 快指针走 2 步
}

慢指针(slow)每次走 1 步,快指针(fast)每次走 2 步,当 fast 走到链表末尾时,slow 恰好走到链表后半段的起点,pre 始终记录 slow 的前一个节点,用于后续分割链表。

3. 分割链表 + 反转后半段
pre.next = null; // 切断前半段和后半段的连接
// 前半段链表的头
ListNode cur1 = head;
// 反转后半段链表,得到反转后的头
ListNode cur2 = reverseList(slow);
辅助方法(reverseList)
ListNode reverseList(ListNode head) {
    ListNode tmp = null; // 临时存储下一个节点
    ListNode pre = null; // 记录当前节点的前一个节点
    while (head != null) {
        tmp = head.next; // 先保存下一个节点,避免断链
        head.next = pre; // 当前节点指向前一个节点(反转)
        pre = head; // pre 后移到当前节点
        head = tmp; // head 后移到原本的下一个节点
    }
    return pre; // 反转后的链表头
}
4. 对比前后两段链表
while (cur1 != null) {
    if (cur1.val != cur2.val) {
        return false;
    }
    cur1 = cur1.next;
    cur2 = cur2.next;
}
return true;

逐一比较两个链表的节点,值只要有一个值不相等,就不是回文;全部相等则是回文。

完整代码
class ListNode {
    int val;
    ListNode next;
    ListNode() {}
    ListNode(int val) { this.val = val; }
    ListNode(int val, ListNode next) { this.val = val; this.next = null; }
}

class Solution {
    public boolean isPalindrome(ListNode head) {
        if (head == null || head.next == null) return true;
        ListNode slow = head;
        ListNode fast = head;
        ListNode pre = head;
        while (fast != null && fast.next != null) {
            pre = slow;
            slow = slow.next;
            fast = fast.next.next;
        }
        pre.next = null;
        ListNode cur1 = head;
        ListNode cur2 = reverseList(slow);
        while (cur1 != null) {
            if (cur1.val != cur2.val) {
                return false;
            }
            cur1 = cur1.next;
            cur2 = cur2.next;
        }
        return true;
    }
    
    ListNode reverseList(ListNode head) {
        ListNode tmp = null;
        ListNode pre = null;
        while (head != null) {
            tmp = head.next;
            head.next = pre;
            pre = head;
            head = tmp;
        }
        return pre;
    }
}

目录

  1. 题目解读
  2. 思路一
  3. 具体步骤与解析
  4. 1. 统计链表长度
  5. 2. 将链表值存入数组
  6. 3. 双指针判断回文
  7. 核心思路总结
  8. 完整代码
  9. 思路二
  10. 具体步骤与解析
  11. 1. 处理边界情况
  12. 2. 快慢指针找链表中点
  13. 3. 分割链表 + 反转后半段
  14. 辅助方法(reverseList)
  15. 4. 对比前后两段链表
  16. 完整代码

更多推荐文章

查看全部
  • GitHub 7 款 Claude Skills 开源项目:Skill Creator、Superpowers 与 Code Review 实战指南
  • 6 款免费 AI 写作软件测评及去 AI 味方案
  • Stable Diffusion v1.5 创意设计师指南:嵌入 Figma/PS 工作流
  • 基于纯 CSS 实现简洁名片卡片设计
  • Linux 进程间通信:匿名管道原理与实现
  • Kimi 新模型 K2.5 多模态与编程能力实测
  • 零基础学微信小程序前端(原生JS):从0到1写第一个可交互页面
  • CoppeliaSim 分拣机器人与寻迹小车仿真:码垛、颜色识别及随机物块处理
  • Git 与 GitHub 入门指南:版本控制与协作实战
  • Java JDK 内置 ZIP 压缩与解压流示例
  • Python 网络数据采集工具源码及开源项目推荐
  • 大模型领域热门岗位解析与求职指南
  • CoPaw 个人助理部署与使用指南:从零搭建专属 AI 数字搭档
  • UTF-8 表情符号及 Web 表情编码翻译表
  • FPGA 实现 CAN 总线:原理与 Verilog 代码详解
  • 利用 AI 编程工具零代码开发小红书卡片 MCP
  • OpenClaw 跨平台安装指南:Windows、macOS 及 Linux 环境配置
  • Spec-Kit 与 Copilot 实现 AI 规格驱动开发
  • 基于 Web Components 的跨框架组件库实践
  • 常见 Web 漏洞渗透及防护方法

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online