力扣hot100之234.回文链表
力扣hot100之234.回文链表

LeetCode 234. 回文链表

题目描述

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。

如果一个链表从前往后读和从后往前读都一样,那么它就是回文链表。


示例分析

示例 1

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

因为从前往后读是:

1,2,2,1

从后往前读也是:

1,2,2,1

所以它是回文链表。


示例 2

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

从前往后读是:

1,2

从后往前读是:

2,1

两者不一样,所以不是回文链表。


解题思路

这道题最直接的一种做法,就是先把链表中的所有节点值取出来,存进一个数组(或者 List)中。

这样问题就从:

判断链表是否是回文

转化成了:

判断一个数组是否是回文

而判断数组是否是回文就很简单了,只需要使用双指针:

  • 一个指针从左往右走

  • 一个指针从右往左走

  • 依次比较对应位置的值是否相等

如果所有位置都相等,那么就是回文;只要有一对不相等,就不是回文。

你给出的代码正是这个思路。


为什么这道题可以先转成数组

链表本身的特点是:

  • 只能从前往后访问

  • 不能像数组那样通过下标随机访问

而“回文”这个问题,本质上需要我们同时比较:

  • 第 1 个元素和最后 1 个元素

  • 第 2 个元素和倒数第 2 个元素

  • 第 3 个元素和倒数第 3 个元素

对于数组来说,这非常容易。 但对于单链表来说,如果每次都想找到“倒数第几个节点”,会很麻烦。

所以先把链表值存进数组,等于是把问题转化成了更容易处理的形式。

这是一种非常常见的思路:

如果链表本身不方便操作,就先提取值,转化成更容易处理的数据结构。


代码实现

class Solution {
   public boolean isPalindrome(ListNode head) {
       List<Integer> vals = new ArrayList<Integer>();
       ListNode currentNode = head;
       while (currentNode != null) {
           vals.add(currentNode.val);
           currentNode = currentNode.next;
      }
       int front = 0;
       int back = vals.size() - 1;
       while (front < back) {
           if (!vals.get(front).equals(vals.get(back))) {
               return false;
          }
           front++;
           back--;
      }
       return true;
  }
}

代码解析

1. 用列表保存链表中的所有值

List<Integer> vals = new ArrayList<Integer>();
ListNode currentNode = head;
while (currentNode != null) {
   vals.add(currentNode.val);
   currentNode = currentNode.next;
}

这段代码做的事情很简单:

  • 从链表头开始遍历

  • 每访问到一个节点,就把它的值加入列表 vals

  • 遍历结束后,vals 中就保存了链表的完整顺序

比如链表:

1 -> 2 -> 2 -> 1

遍历结束后:

vals = [1, 2, 2, 1]

这样后面就可以像操作数组一样去判断它是不是回文。


2. 定义头尾双指针

int front = 0;
int back = vals.size() - 1;
  • front 指向数组最左边

  • back 指向数组最右边

接下来不断比较它们对应的值。


3. 从两端向中间比较

while (front < back) {
    if (!vals.get(front).equals(vals.get(back))) {
        return false;
    }
    front++;
    back--;
}

如果左边和右边的值不同,就说明不是回文,直接返回 false

如果相同,就继续向中间收缩。

为什么循环条件是:

front < back

因为当两个指针相遇或者交错时,说明整个数组已经比较完了。


4. 全部比较完成后返回 true

return true;

说明所有对应位置的值都相等,因此链表是回文链表。


示例推演

我们以这个链表为例:

1 -> 2 -> 2 -> 1

第一步:提取链表值

遍历后得到:

vals = [1, 2, 2, 1]

第二步:双指针比较

初始状态:

front = 0
back = 3

比较:

vals[0] = 1
vals[3] = 1

相等,继续。

然后:

front = 1
back = 2

比较:

vals[1] = 2
vals[2] = 2

相等,继续。

接着:

front = 2
back = 1

此时 front < back 不成立,循环结束。

说明整个数组是回文,所以返回 true


再看一个不是回文的例子

链表:

1 -> 2

提取后:

vals = [1, 2]

双指针比较:

  • front = 0

  • back = 1

比较得到:

vals[0] = 1
vals[1] = 2

不相等,所以直接返回 false


为什么这里要用 equals 而不是 ==

代码里写的是:

if (!vals.get(front).equals(vals.get(back))) {

这里使用 equals 是因为 vals 中存的是 Integer 对象,而不是基本类型 int

在 Java 中,比较两个对象的值是否相等,应该优先使用 equals

如果用 ==,比较的是对象引用地址,不够稳妥。

所以这里写 equals 是更规范、更安全的做法。


这种做法的优点

这份代码的优点非常明显:

1. 思路直观

先遍历链表存值,再判断数组是否回文,非常容易理解。

2. 写法简单

代码量不大,逻辑清晰,适合这道题的入门思路。

3. 不会破坏原链表结构

有些进阶做法会去反转链表后半部分,而这种做法只是读取链表值,不会修改原链表。


这种做法的缺点

缺点也很明确:

需要额外的 O(n) 空间。

因为我们开了一个 List<Integer> 来保存所有节点值。

如果题目进一步要求:

  • 时间复杂度 O(n)

  • 空间复杂度 O(1)

那么这种方法就不够优了。

那时通常要用:

  • 快慢指针找中点

  • 反转后半部分链表

  • 再比较前后两段

但从易理解和易实现的角度来说,你这份代码已经是非常好的基础解法。


复杂度分析

设链表长度为 n

时间复杂度

O(n)
  • 第一次遍历链表,把值加入数组,需要 O(n)

  • 第二次用双指针比较数组,需要 O(n)

所以总时间复杂度仍然是 O(n)


空间复杂度

O(n)

因为额外使用了一个列表来保存链表中的所有值。


和进阶做法对比

当前这种“数组 + 双指针”做法

优点:

  • 思路最直观

  • 代码简单

  • 不会修改链表结构

缺点:

  • 需要 O(n) 额外空间


进阶做法:快慢指针 + 反转后半链表

优点:

  • 空间复杂度可以做到 O(1)

缺点:

  • 实现更复杂

  • 需要处理链表反转和还原的问题

  • 更容易写错

所以如果只是先把题做出来,当前这份代码已经很合适。 如果是面试追求更优空间复杂度,再去掌握进阶做法会更好。


总结

这道题的核心思路其实很简单:

  1. 先遍历链表,把所有节点值保存到数组中

  2. 再用双指针从两端向中间比较

  3. 只要有一对值不相等,就不是回文

  4. 如果全部相等,就是回文链表

你可以把这题总结成一句话:

先把链表转成数组,再把链表回文问题转化成数组回文问题。

这是一种非常典型的“降维处理”思路,虽然不是最省空间的写法,但非常清晰、稳定,也很适合作为这道题的第一种解法来掌握。


如果对您有帮助的话,能否支持一下博主?
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇