回文链表(LeetCode 234)C语言最佳解题思路

最优思路(原地反转后半段,空间 O (1))

核心 4 步流程:

  1. 快慢指针:找到链表中点(快指针走两步,慢一步,快到末尾时慢在中点)
  2. 反转后半部分链表(从中点下一个节点开始反转)
  3. 双指针同步比对:前半从头、后半从反转后的头逐节点比较
cs 复制代码
#include <stdio.h>
#include <stdlib.h>

// 链表节点定义
struct ListNode {
    int val;
    struct ListNode *next;
};

// 函数1:反转链表(输入头,返回反转后的新头)
struct ListNode* reverseList(struct ListNode* head) {
    struct ListNode *pre = NULL;  // 前驱节点初始空
    struct ListNode *cur = head; // 当前遍历节点
    struct ListNode *temp;        // 临时存下一个节点
    
    while (cur != NULL) {
        temp = cur->next;  // 先保存下一个节点,防止断链
        cur->next = pre;   // 当前节点反向指向前驱
        pre = cur;         // 前驱后移
        cur = temp;        // 当前节点后移
    }
    return pre; // pre最后指向反转后的头节点
}

// 函数2:主逻辑 判断回文
bool isPalindrome(struct ListNode* head) {
    // 边界:空链表/单节点一定是回文
    if (head == NULL || head->next == NULL) {
        return true;
    }

    // 步骤1:快慢指针找中点
    struct ListNode *slow = head;  // 慢指针,一步一格
    struct ListNode *fast = head;   // 快指针,两步一格
    // fast->next && fast->next->next 保证偶数/奇数链表都停在中点前
    while (fast->next != NULL && fast->next->next != NULL) {
        slow = slow->next;
        fast = fast->next->next;
    }

    // 步骤2:反转后半段,slow->next是后半段起点
    struct ListNode *secondHalfHead = reverseList(slow->next);
    // 断开前后半段(避免奇数链表中点干扰比对)
    slow->next = NULL;

    // 步骤3:双指针比对前后两段
    struct ListNode *p1 = head;             // 前半段头
    struct ListNode *p2 = secondHalfHead;   // 反转后的后半段头
    while (p2 != NULL) { // p2更短,以p2遍历结束为准
        if (p1->val != p2->val) {
            result = false;
        }
        p1 = p1->next;
        p2 = p2->next;
    }

    return result;
}

while (fast->next && fast->next->next) {

这个循环的目的:让 slow 停在前半段最后一个节点,slow->next 就是后半段起点,奇数、偶数链表都适配。 fast 一次走两步,slow 一次走一步,循环条件控制 fast 不能越界。分两种链表演示(奇数长度 / 偶数长度)

情况 1:偶数链表 1,2,2,1 长度 4

节点:1 → 2 → 2 → 1 初始:slow=1,fast=1

第一轮判断: fast->next=2,fast->next->next=2,都不为空 → 进入循环 slow=2,fast=2(fast 一次跳两步到第三个节点 2)

再次判断: fast->next=1,fast->next->next=NULL fast->next->next 是空 → 条件不成立,退出循环。

最终:slow 停在第二个节点 2(前半段末尾),后半段起点 slow->next = 第三个节点 2。 前半:1,2;后半:2,1。完美对半分。

情况 2:奇数链表 1,2,3,2,1 长度 5

节点:1→2→3→2→1 初始 slow=1, fast=1

第一轮:fast 后有 2、3,满足条件 slow=2,fast=3

再判断:fast->next=2,fast->next->next=1 满足,再走一轮 slow=3,fast=1

再判断:fast->next=NULL,条件失效,退出循环。

最终 slow 停在中间节点 3,后半段起点 slow->next = 2。 前半:1,2,3;后半:2,1。比对时只对比短的后半段,中间奇数节点不用管。

slow->next = NULL; // 步骤3:双指针比对前后两段

struct ListNode *p1 = head;

slow和head都是两个带着地址的点,不是整个链表,所以p1应该等于head,而不是slow。

相关推荐
铅笔小新z8 分钟前
【C语言】深入理解指针
c语言·开发语言
Nil2084 小时前
leetcode 300最长递增子序列
算法·leetcode·职场和发展
yi0116 小时前
LeetCode 424. 替换后的最长重复字符|不定长滑动窗口:如何判断窗口是否合法?
笔记·python·算法·leetcode·双指针·滑动窗口
All for pursuit.6 小时前
【动态规划-11】279.完全平方数
数据结构·c++·算法·leetcode·动态规划
Aurorar0rua7 小时前
CS50 x 2024 Notes Data structures - 08
c语言·开发语言·学习方法
小米里的大麦7 小时前
19 C、C++ 操作 MySQL
c语言·c++·mysql
Logic1017 小时前
C语言/数据结构位运算题解:异或XOR找出套碗游戏中的“幸运碗“——只出现一次的数字
c语言·数据结构·算法·数组·位运算·时间复杂度·空间复杂度
番茄灭世神7 小时前
PN学堂GD32教程第12篇——SPI与Nor Flash
c语言·stm32·单片机·嵌入式·gd32·总线协议
yi0118 小时前
LeetCode 3. 无重复字符的最长子串|不定长滑动窗口:什么时候更新最大值?
笔记·python·算法·leetcode·双指针·滑动窗口
y1su8 小时前
Leetcode 单调栈模板
java·开发语言·数据结构·算法·leetcode