三种解法(数组、栈、快慢指针)全面解析——力扣234.回文链表全解析
力扣234.回文链表全解析
一、题目描述
给你一个单链表的头节点 head
,请你判断该链表是否为回文链表。
如果是,返回 true
;否则,返回 false
。
示例 1:
输入:head = [1,2,2,1]
输出:true
示例 2:
输入:head = [1,2]
输出:false
提示:
- 链表中节点数目在范围
[1, 10⁵]
内 0 <= Node.val <= 9
进阶要求:
你能否只用 O(n) 时间复杂度 和 O(1) 空间复杂度解决此题?
二、思路总览
判断链表是否为回文,本质是要看:
链表从左往右与从右往左读是否相同。
我们可以分三种方法逐步优化:
方法 | 思路 | 时间复杂度 | 空间复杂度 |
---|---|---|---|
方法一 | 转成数组后双指针比较 | O(n) | O(n) |
方法二 | 栈辅助比较 | O(n) | O(n) |
方法三 | 快慢指针 + 反转链表 | O(n) | O(1) |
下面我们逐一讲解。
三、方法一:转为数组法(思路直观)
思路
- 遍历链表,把所有节点值存入数组;
- 用双指针从头尾同时向中间比较;
- 若发现不相等,返回
false
;否则返回true
。
代码实现
class Solution {public boolean isPalindrome(ListNode head) {List<Integer> list = new ArrayList<>();ListNode curr = head;while (curr != null) {list.add(curr.val);curr = curr.next;}int left = 0, right = list.size() - 1;while (left < right) {if (!list.get(left).equals(list.get(right))) {return false;}left++;right--;}return true;}
}
复杂度分析
- 时间复杂度:O(n)
- 空间复杂度:O(n)
四、方法二:栈法(利用后进先出特性)
思路
- 遍历链表,将所有节点压入栈;
- 再次遍历链表,每次弹出一个元素进行比较;
- 若有不一致则返回
false
。
代码实现
class Solution {public boolean isPalindrome(ListNode head) {Stack<Integer> stack = new Stack<>();ListNode curr = head;while (curr != null) {stack.push(curr.val);curr = curr.next;}curr = head;while (curr != null) {if (curr.val != stack.pop()) {return false;}curr = curr.next;}return true;}
}
复杂度分析
- 时间复杂度:O(n)
- 空间复杂度:O(n)
五、方法三:快慢指针 + 链表反转(最优解)
核心思想
利用“反转链表”的技巧来节省空间:
-
使用快慢指针
fast
、slow
:fast
一次走两步,slow
一次走一步;- 当
fast
到达末尾时,slow
到达中点。
-
反转后半部分链表。
-
从头和中点开始同时遍历,比较对应节点值是否相等。
-
可选:比较完成后再把链表恢复原状(面试时可选)。
图解思路
以链表 [1, 2, 2, 1]
为例:
-
找到中点:
slow
停在第二个2
; -
反转右半部分:
[2, 1] → [1, 2]
; -
比较前后两半:
1 == 1 2 == 2
-
全部相等 → 回文。
class Solution {public boolean isPalindrome(ListNode head) {if (head == null || head.next == null) return true;// 1. 快慢指针找中点ListNode slow = head, fast = head;while (fast != null && fast.next != null) {slow = slow.next;fast = fast.next.next;}// 2. 反转后半部分ListNode secondHalf = reverseList(slow);// 3. 比较两半ListNode p1 = head, p2 = secondHalf;boolean isPalin = true;while (p2 != null) {if (p1.val != p2.val) {isPalin = false;break;}p1 = p1.next;p2 = p2.next;}// 4. 可选:恢复链表结构// reverseList(secondHalf);return isPalin;}private ListNode reverseList(ListNode head) {ListNode prev = null, curr = head;while (curr != null) {ListNode next = curr.next;curr.next = prev;prev = curr;curr = next;}return prev;}
}
复杂度分析
- 时间复杂度:O(n)
- 空间复杂度:O(1)
六、总结对比
方法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
---|---|---|---|---|
数组法 | O(n) | O(n) | 简单易懂 | 占空间 |
栈法 | O(n) | O(n) | 直观好理解 | 同样占空间 |
快慢指针 | O(n) | O(1) | 最优、工程推荐 | 实现略复杂 |
七、总结一句话
回文链表的核心是 左右对称。
你可以用数组存储、用栈回溯,或者直接在链表内部用指针完成空间优化。
理解结构关系比死记代码更重要。