news 2026/3/8 14:31:44

力扣234.回文链表-反转后半链表

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
力扣234.回文链表-反转后半链表

问题描述

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

示例 :

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

进阶:你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

解法一:反转后半部分链表(最优解)

这是面试中最常考的方法,时间复杂度 O(n),空间复杂度 O(1)。

算法步骤

  1. 使用快慢指针找到链表的中间节点

  2. 反转链表的后半部分

  3. 比较前半部分和反转后的后半部分

  4. 恢复链表(可选)

代码实现

class Solution { public boolean isPalindrome(ListNode head) { if(head==null||head.next==null){ return true; } ListNode mid=Find_mid(head); ListNode head2=reverse_List(mid); while(head2!=null){ if(head.val!=head2.val){ return false; } head=head.next; head2=head2.next; } return true; } private ListNode reverse_List(ListNode head){//反转链表 ListNode pre=null; ListNode cur=head; while(cur!=null){ ListNode Temp=cur.next; cur.next=pre; pre=cur; cur=Temp; } return pre; } private ListNode Find_mid(ListNode head){//找到中间节点 ListNode slow=head; ListNode fast=head; while(fast!=null&&fast.next != null){ slow=slow.next; fast=fast.next.next; } return slow; } }

关键点分析

  1. 快慢指针找中点

    • 慢指针每次走一步,快指针每次走两步

    • 当快指针到达末尾时,慢指针正好在中点

    • 对于奇数长度链表,慢指针停在中间节点

    • 对于偶数长度链表,慢指针停在中间两个节点的第二个

  2. 反转链表

    • 使用三个指针:pre、curr、Temp,cur指向pre,pre往前移,cur往前移

    • 每次迭代将当前节点的next指向前一个节点

时间复杂度与空间复杂度

  • 时间复杂度:O(n)

    • 找中点:O(n/2) ≈ O(n)

    • 反转后半部分:O(n/2) ≈ O(n)

    • 比较两部分:O(n/2) ≈ O(n)

    • 总时间:O(n)

  • 空间复杂度:O(1)

    • 只使用了常数级别的额外空间

解法二:使用栈

思路

利用栈的后进先出特性,将链表元素入栈,然后依次出栈与链表比较。

代码实现

java

class Solution { public boolean isPalindrome(ListNode head) { if (head == null || head.next == null) { return true; } Stack<Integer> stack = new Stack<>(); ListNode current = head; // 将链表值压入栈中 while (current != null) { stack.push(current.val); current = current.next; } // 比较栈顶元素和链表当前值 current = head; while (current != null) { if (current.val != stack.pop()) { return false; } current = current.next; } return true; } }

复杂度分析

  • 时间复杂度:O(n),需要遍历链表两次

  • 空间复杂度:O(n),需要额外栈空间

解法三:递归

思路

利用递归的调用栈,从链表末尾开始比较。

代码实现

java

class Solution { private ListNode frontPointer; public boolean isPalindrome(ListNode head) { frontPointer = head; return recursivelyCheck(head); } private boolean recursivelyCheck(ListNode currentNode) { if (currentNode != null) { // 递归到链表末尾 if (!recursivelyCheck(currentNode.next)) { return false; } // 比较当前节点值和前端指针的值 if (currentNode.val != frontPointer.val) { return false; } // 前端指针向后移动 frontPointer = frontPointer.next; } return true; } }

复杂度分析

  • 时间复杂度:O(n),需要递归遍历整个链表

  • 空间复杂度:O(n),递归调用栈的空间

解法四:复制到数组 + 双指针

思路

将链表值复制到数组中,然后使用双指针判断数组是否为回文。

代码实现

java

class Solution { public boolean isPalindrome(ListNode head) { // 将链表值复制到数组中 List<Integer> values = new ArrayList<>(); ListNode current = head; while (current != null) { values.add(current.val); current = current.next; } // 使用双指针判断数组是否为回文 int left = 0; int right = values.size() - 1; while (left < right) { if (!values.get(left).equals(values.get(right))) { return false; } left++; right--; } return true; } }

复杂度分析

  • 时间复杂度:O(n)

    • 复制到数组:O(n)

    • 双指针比较:O(n/2) ≈ O(n)

  • 空间复杂度:O(n),需要额外数组存储链表值

常见错误与注意事项

错误1:没有处理奇偶长度差异

java

// 错误示例:没有考虑奇偶长度差异 private ListNode findMiddle(ListNode head) { ListNode slow = head; ListNode fast = head; while (fast != null) { // 错误:应该检查fast.next slow = slow.next; fast = fast.next.next; } return slow; }

修正

java

private ListNode findMiddle(ListNode head) { ListNode slow = head; ListNode fast = head; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; } return slow; }

错误2:反转链表实现错误

修正

java

private ListNode reverseList(ListNode head) { ListNode pre = null; ListNode cur = head; while (cur != null) { ListNode temp = cur.next; cur.next = pre; pre = cur; cur = temp; } return pre; }

扩展:如果要恢复链表怎么办?

如果需要保持链表原样,可以在比较后再次反转恢复:

java

class Solution { public boolean isPalindrome(ListNode head) { if (head == null || head.next == null) return true; // 找到中点 ListNode slow = head, fast = head; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; } // 反转后半部分 ListNode secondHalf = reverseList(slow); // 比较 ListNode p1 = head, p2 = secondHalf; boolean result = true; while (p2 != null) { if (p1.val != p2.val) { result = false; break; } p1 = p1.next; p2 = p2.next; } // 恢复链表 reverseList(secondHalf); return result; } private ListNode reverseList(ListNode head) { ListNode prev = null, curr = head; while (curr != null) { ListNode nextTemp = curr.next; curr.next = prev; prev = curr; curr = nextTemp; } return prev; } }

总结

方法时间复杂度空间复杂度优点缺点
反转后半部分O(n)O(1)空间最优,满足进阶要求修改了原链表结构
使用栈O(n)O(n)实现简单,不修改原链表需要额外空间
递归O(n)O(n)代码简洁递归深度可能较大
复制到数组O(n)O(n)实现简单需要额外空间

推荐使用反转后半部分链表的方法,因为:

  1. 空间复杂度为 O(1),满足进阶要求

  2. 时间复杂度为 O(n),性能良好

  3. 是面试中最常考的解法

相关题目

  1. 反转链表:基础中的基础,必须掌握

  2. 链表的中间结点:快慢指针的经典应用

  3. 回文数字:类似的回文判断问题

  4. 回文字符串:字符串版本的回文判断

掌握这道题的关键在于理解快慢指针和链表反转这两个核心技巧,这两个技巧在链表相关题目中非常常见。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/5 4:56:28

AI不是低代码的“救世主”,却是破局的“催化剂”

“上周用低代码搭了个设备管理系统&#xff0c;这周业务改需求&#xff0c;改到一半发现组件逻辑冲突&#xff0c;最后还是喊后端重写了核心模块。”在某制造业数字化转型交流会上&#xff0c;一位IT主管的吐槽道出了许多从业者的困境。曾被寄予“全民开发”厚望的低代码&#…

作者头像 李华
网站建设 2026/3/1 14:16:07

基于PSO-SVR和NSGA-Ⅲ的高温合金冷成形螺栓工艺优化MATLAB代码

一、 背景挑战 高温合金&#xff1a;如Inconel 718、GH4169等&#xff0c;具有优异的高温强度、抗蠕变和耐腐蚀性能&#xff0c;广泛应用于航空航天发动机螺栓等关键连接件。冷成形工艺&#xff1a;在室温下通过塑性变形&#xff08;如镦锻、滚丝&#xff09;制造螺栓。其优点是…

作者头像 李华
网站建设 2026/3/7 18:20:20

37、Ubuntu硬盘手动分区及相关资源指南

Ubuntu硬盘手动分区及相关资源指南 手动分区概述 在使用Ubuntu安装程序对硬盘进行分区时,前两个选项(使用整个磁盘和调整现有分区大小)通常能满足大多数情况。但如果你想更深入地操作,也有一些特别的分区方式。比如,你可以创建一个共享数据分区,让Windows和Ubuntu能共享…

作者头像 李华
网站建设 2026/3/1 5:19:49

[安全测试】appscan下载与安装

一、AppScan 下载 我的百度网盘下载地址&#xff1a;https://pan.baidu.com/s/1mf4z74mvlPMwO_vd7KC2nw 提取码&#xff1a;FXZZ二、AppScan 安装 &#xff08;1&#xff09;双击 “AppScan_Setup_10.0.0.exe” 开始安装学习资源 如果你是也准备转行学习网络安全&#xff08;黑…

作者头像 李华
网站建设 2026/3/7 19:21:27

Servlet 网页重定向

Servlet 网页重定向 引言 在Web开发中,Servlet是Java EE技术中用于处理客户端请求和服务器响应的关键组件。网页重定向是Servlet技术中的一个重要功能,它允许服务器在处理完请求后,将用户导向另一个URL。本文将详细介绍Servlet网页重定向的概念、原理以及实现方法。 一、…

作者头像 李华