leetcode
leetcode 201 ~ 250
回文链表

回文链表

难度:

标签:

题目描述

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

 

示例 1:

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

示例 2:

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

 

提示:

  • 链表中节点数目在范围[1, 105]
  • 0 <= Node.val <= 9

 

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

代码结果

运行时间: 76 ms, 内存: 24.8 MB


/*
 * 判断单链表是否为回文链表
 * 思路:
 * 1. 使用流的方式将链表元素收集到列表中。
 * 2. 使用双指针法从列表两端向中间比较。
 */
import java.util.*;
import java.util.stream.*;
class ListNode {
    int val;
    ListNode next;
    ListNode(int val) { this.val = val; this.next = null; }
}
public class Solution {
    public boolean isPalindrome(ListNode head) {
        if (head == null || head.next == null) return true;
        // 将链表元素收集到列表中
        List<Integer> values = new ArrayList<>();
        ListNode current = head;
        while (current != null) {
            values.add(current.val);
            current = current.next;
        }
        // 使用双指针法比较列表两端的元素
        int left = 0, right = values.size() - 1;
        while (left < right) {
            if (!values.get(left).equals(values.get(right))) return false;
            left++;
            right--;
        }
        return true;
    }
}

解释

方法:

该题解的思路是将链表分为两半,反转后半部分链表,然后同时遍历前半部分和反转后的后半部分,依次比较对应节点的值是否相等。首先通过快慢指针找到链表的中点,慢指针每次走一步,快指针每次走两步,当快指针到达链表尾部时,慢指针刚好到达中点。如果链表节点数为奇数,则中点后一个节点才是后半部分的起点。然后反转后半部分链表,再同时遍历比较两个链表的节点值是否相等,如果都相等则说明是回文链表,否则不是回文链表。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
在找到链表中点时,快慢指针的策略是如何确保慢指针停在正确的中点位置,特别是在链表长度为奇数和偶数时的具体差异是什么?
快慢指针策略中,快指针每次移动两步,慢指针每次移动一步。当快指针无法继续移动(即到达链表尾部)时,慢指针则正好位于中点。对于偶数节点的链表,当快指针的下一个节点为null时,慢指针位于中间两个节点的第一个;对于奇数节点的链表,当快指针自身为null时,慢指针位于中间节点。因此,在奇数长度的链表中,为了使后半部分与前半部分长度相同,需要将慢指针向前移动一位,从而跳过正中间的节点。
🦆
在反转链表部分,如何处理只有一个节点的情况,以及这种情况下反转后的链表头是什么?
当链表只有一个节点时,反转操作非常简单。在反转函数中,该节点会被设置为当前节点(cur),而前一个节点(pre)被设置为null。遍历开始时,将当前节点的next指针指向前一个节点,即null,然后更新前节点为当前节点,当前节点移动到下一个节点,即null。因此,反转后的链表头仍然是原链表的唯一节点,其next指针指向null,表示链表结束。
🦆
您提到如果链表节点数为奇数,则中点后一个节点才是后半部分的起点,为什么需要跳过中点,它对回文结构的影响是什么?
在判断回文链表时,如果链表的节点数为奇数,中间的节点不需要与任何节点比较,因为它自身就是对称的中心。因此,为了简化比较过程,我们可以跳过这个中间节点,直接从它的下一个节点开始反转后半部分链表。这样做可以确保前后两部分长度相等,便于直接比较。如果包括中间节点在内,前后两部分的长度将不一致,无法进行有效的一对一比较。
🦆
在比较两个链表的节点值时,比较结束的条件是什么?为什么不需要同时检查左半部分链表是否也已遍历完毕?
比较两个链表的节点值时,结束条件是右半部分(已被反转的后半部分)的链表节点被完全遍历完毕。由于反转后的右半部分链表长度与左半部分链表长度相同(或在奇数节点链表中略短一节点),因此,当右半部分遍历完毕时,左半部分链表对应的部分也刚好遍历完毕。这确保了每个节点都被比较过,无需单独检查左半部分是否遍历完毕。

相关问题

回文数

给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false

回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

  • 例如,121 是回文,而 123 不是。

 

示例 1:

输入:x = 121
输出:true

示例 2:

输入:x = -121
输出:false
解释:从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。

示例 3:

输入:x = 10
输出:false
解释:从右向左读, 为 01 。因此它不是一个回文数。

 

提示:

  • -231 <= x <= 231 - 1

 

进阶:你能不将整数转为字符串来解决这个问题吗?

验证回文串

如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串

字母和数字都属于字母数字字符。

给你一个字符串 s,如果它是 回文串 ,返回 true ;否则,返回 false

 

示例 1:

输入: s = "A man, a plan, a canal: Panama"
输出:true
解释:"amanaplanacanalpanama" 是回文串。

示例 2:

输入:s = "race a car"
输出:false
解释:"raceacar" 不是回文串。

示例 3:

输入:s = " "
输出:true
解释:在移除非字母数字字符之后,s 是一个空字符串 "" 。
由于空字符串正着反着读都一样,所以是回文串。

 

提示:

  • 1 <= s.length <= 2 * 105
  • s 仅由可打印的 ASCII 字符组成

反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

 

示例 1:

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例 2:

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

示例 3:

输入:head = []
输出:[]

 

提示:

  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000

 

进阶:链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?