【每日算法】LeetCode 234. 回文链表详解

对前端开发者而言,学习算法绝非为了"炫技"。它是你从"页面构建者"迈向"复杂系统设计者"的关键阶梯。它将你的编码能力从"实现功能"提升到"设计优雅、高效解决方案"的层面。从现在开始,每天投入一小段时间,结合前端场景去理解和练习,你将会感受到自身技术视野和问题解决能力的质的飞跃。------ 算法:资深前端开发者的进阶引擎

LeetCode 234. 回文链表

1. 题目描述

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

示例 1:

复制代码
输入: head = [1,2,2,1]
输出: true

示例 2:

复制代码
输入: head = [1,2]
输出: false

进阶要求: 尝试使用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题。

2. 问题分析

回文链表是指链表节点值从前往后读和从后往前读完全一致。作为前端开发者,我们常处理类似 DOM 树或组件状态树的结构,链表作为一种线性数据结构,在内存管理和优化中具有参考价值。

核心挑战:

  • 链表单向遍历,无法直接反向访问。
  • 需要在有限空间内高效比较节点值。
  • 进阶要求 O(1) 空间,排除使用额外数组或栈等线性空间。

前端关联场景: 例如,在虚拟 DOM 差异算法或状态历史管理中,检查结构对称性可优化渲染性能。

3. 解题思路

3.1 思路一:转换为数组法

将链表值复制到数组,再用双指针从两端向中间比较回文。

  • 时间复杂度: O(n)
  • 空间复杂度: O(n)
  • 优点: 简单直观,易于实现。
  • 缺点: 额外 O(n) 空间,不满足进阶要求。

3.2 思路二:递归法

利用递归栈隐式存储节点,从链表两端向内比较。

  • 时间复杂度: O(n)
  • 空间复杂度: O(n)(递归调用栈)
  • 优点: 代码简洁,体现递归思想。
  • 缺点: 栈空间 O(n),可能栈溢出,不适合长链表。

3.3 思路三:快慢指针反转后半部分法(最优解)

使用快慢指针找到链表中点,反转后半部分链表,再比较前后两半是否一致。最后可选恢复链表。

  • 时间复杂度: O(n)
  • 空间复杂度: O(1)
  • 优点: 满足进阶要求,时间 O(n)、空间 O(1)。
  • 缺点: 修改链表结构,但可恢复。

4. 各思路代码实现

4.1 思路一:转换为数组法

javascript 复制代码
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
function isPalindrome(head) {
    const arr = [];
    let curr = head;
    while (curr !== null) {
        arr.push(curr.val);
        curr = curr.next;
    }
    let left = 0, right = arr.length - 1;
    while (left < right) {
        if (arr[left] !== arr[right]) return false;
        left++;
        right--;
    }
    return true;
}

4.2 思路二:递归法

javascript 复制代码
function isPalindrome(head) {
    let frontPointer = head;
    
    function recursivelyCheck(currentNode) {
        if (currentNode !== null) {
            if (!recursivelyCheck(currentNode.next)) return false;
            if (currentNode.val !== frontPointer.val) return false;
            frontPointer = frontPointer.next;
        }
        return true;
    }
    
    return recursivelyCheck(head);
}

4.3 思路三:快慢指针反转后半部分法

javascript 复制代码
function isPalindrome(head) {
    if (head === null || head.next === null) return true;
    
    // 快慢指针找中点
    let slow = head, fast = head;
    while (fast.next !== null && fast.next.next !== null) {
        slow = slow.next;
        fast = fast.next.next;
    }
    
    // 反转后半部分链表
    let secondHalfStart = reverseList(slow.next);
    
    // 比较前后两半
    let p1 = head, p2 = secondHalfStart;
    let isPal = true;
    while (p2 !== null) {
        if (p1.val !== p2.val) {
            isPal = false;
            break;
        }
        p1 = p1.next;
        p2 = p2.next;
    }
    
    // 恢复链表(可选,保持原结构)
    slow.next = reverseList(secondHalfStart);
    
    return isPal;
}

// 辅助函数:反转链表
function reverseList(head) {
    let prev = null, curr = head;
    while (curr !== null) {
        const nextTemp = curr.next;
        curr.next = prev;
        prev = curr;
        curr = nextTemp;
    }
    return prev;
}

5. 各实现思路的复杂度、优缺点对比表格

思路 时间复杂度 空间复杂度 优点 缺点 适用场景
转换为数组法 O(n) O(n) 实现简单,快速原型开发 额外 O(n) 空间,不满足进阶要求 小规模数据或无需空间优化时
递归法 O(n) O(n) 代码简洁,递归思维训练 递归栈 O(n),可能栈溢出,性能较差 学习递归,链表长度有限时
快慢指针反转法 O(n) O(1) 最优解,空间高效,满足进阶要求 需要修改链表(可恢复),实现稍复杂 大规模数据、内存敏感场景

6. 总结

回文链表问题不仅是算法练习,更是前端开发者深化数据结构理解的契机。通过比较不同解法,我们学会在时间与空间之间权衡,这对前端性能优化至关重要。

实际应用场景:

  • 前端状态管理: 如 Redux 或 MobX 中,检查状态变更历史是否对称,以支持撤销/重做功能。
  • 虚拟 DOM 优化: 在 React 等框架中,比较组件树结构是否回文,可减少不必要的渲染。
  • 数据验证: 处理用户输入(如链表形式的嵌套配置)时,验证其对称性。
  • 内存敏感应用: 移动端或低端设备中,O(1) 空间算法能降低内存开销,提升应用流畅度。

作为前端开发者,掌握此类算法将助力你从实现功能转向设计高效系统,提升代码质量和问题解决能力。坚持每日算法练习,结合前端实践,你将在技术道路上走得更远。

相关推荐
NeDon2 小时前
[OJ]数据结构:移除链表元素
c语言·数据结构·算法·链表
刃神太酷啦2 小时前
C++ list 容器全解析:从构造到模拟实现的深度探索----《Hello C++ Wrold!》(16)--(C/C++)
java·c语言·c++·qt·算法·leetcode·list
承渊政道2 小时前
一文彻底搞清楚链表算法实战大揭秘和双向链表实现
c语言·数据结构·算法·leetcode·链表·visual studio
sali-tec2 小时前
C# 基于halcon的视觉工作流-章69 深度学习-异常值检测
开发语言·图像处理·算法·计算机视觉·c#
努力写代码的熊大3 小时前
手撕AVL树:从理论到实践,掌握插入操作的完美平衡
算法
wbs_scy3 小时前
C++:二叉搜索树(BST)完全指南(从概念原理、核心操作到底层实现)
数据结构·算法
东华万里3 小时前
Release 版本禁用 assert:NDEBUG 的底层逻辑与效率优化
java·jvm·算法
liulilittle3 小时前
C++ CRTP 替代虚函数
数据结构·c++·算法