用go实现-回文链表

目录

题目

算法思路

代码

代码详解

[1. 边界处理](#1. 边界处理)

[2. 快慢指针找中点](#2. 快慢指针找中点)

[3. 反转后半部分链表](#3. 反转后半部分链表)

[4. 比较前后两部分](#4. 比较前后两部分)


题目

​​​​​234. 回文链表

简单

给你一个单链表的头节点 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) 空间复杂度解决此题?

算法思路

核心思想:反转后半部分链表进行比较

主要步骤:

  1. 找到中间节点(使用快慢指针)

    • 慢指针每次走一步,快指针每次走两步

    • 当快指针到达末尾时,慢指针在中间位置

  2. 反转后半部分链表

    • 从中间节点开始反转链表后半部分
  3. 比较前后两部分

    • 分别从头节点和反转后的后半部分头节点开始比较

    • 逐个节点比较值是否相等

代码

Go 复制代码
package main

import "fmt"

// 链表节点定义
type ListNode struct {
    Val  int
    Next *ListNode
}

// 解法:反转后半部分链表(O(1)空间复杂度)
func isPalindrome(head *ListNode) bool {
    if head == nil || head.Next == nil {
        return true
    }
    
    // 1. 快慢指针找中点
    slow, fast := head, head
    for fast != nil && fast.Next != nil {
        slow = slow.Next
        fast = fast.Next.Next
    }
    
    // 2. 反转后半部分
    var prev *ListNode
    cur := slow
    for cur != nil {
        next := cur.Next
        cur.Next = prev
        prev = cur
        cur = next
    }
    
    // 3. 比较前后两部分
    p1, p2 := head, prev
    for p2 != nil {
        if p1.Val != p2.Val {
            return false
        }
        p1 = p1.Next
        p2 = p2.Next
    }
    
    return true
}

// 创建测试链表
func createList(nums []int) *ListNode {
    if len(nums) == 0 {
        return nil
    }
    
    head := &ListNode{Val: nums[0]}
    cur := head
    for i := 1; i < len(nums); i++ {
        cur.Next = &ListNode{Val: nums[i]}
        cur = cur.Next
    }
    return head
}

// 测试函数
func main() {
    // 测试用例
    tests := []struct {
        nums     []int
        expected bool
    }{
        {[]int{1, 2, 2, 1}, true},   // 示例1
        {[]int{1, 2}, false},         // 示例2
        {[]int{1}, true},             // 单节点
        {[]int{}, true},              // 空链表
        {[]int{1, 2, 3, 2, 1}, true}, // 奇数长度回文
        {[]int{1, 2, 3, 4}, false},   // 非回文
        {[]int{1, 1}, true},          // 两节点相同
        {[]int{1, 2, 1}, true},       // 三节点回文
    }
    
    // 运行测试
    for i, test := range tests {
        head := createList(test.nums)
        result := isPalindrome(head)
        
        if result == test.expected {
            fmt.Printf("测试用例 %d ✓: %v -> %v\n", i+1, test.nums, result)
        } else {
            fmt.Printf("测试用例 %d ✗: %v -> %v (期望 %v)\n", i+1, test.nums, result, test.expected)
        }
    }
}
Go 复制代码
func isPalindrome(head *ListNode) bool {
    if head == nil || head.Next == nil {
        return true
    }
    
    // 快慢指针找中点
    slow, fast := head, head
    for fast != nil && fast.Next != nil {
        slow = slow.Next
        fast = fast.Next.Next
    }
    
    // 反转后半部分
    var prev *ListNode
    cur := slow
    for cur != nil {
        next := cur.Next
        cur.Next = prev
        prev = cur
        cur = next
    }
    
    // 比较前后两部分
    p1, p2 := head, prev
    for p2 != nil {
        if p1.Val != p2.Val {
            return false
        }
        p1 = p1.Next
        p2 = p2.Next
    }
    
    return true
}

代码详解

1. 边界处理

go

复制代码
if head == nil || head.Next == nil {
    return true
}
  • head == nil:空链表,没有节点,视为回文

  • head.Next == nil :只有一个节点,如 [1],也是回文

  • 这两种情况直接返回 true

2. 快慢指针找中点

go

复制代码
slow, fast := head, head
for fast != nil && fast.Next != nil {
    slow = slow.Next
    fast = fast.Next.Next
}

快慢指针原理

  • slow 慢指针:每次走 1 步

  • fast 快指针:每次走 2 步

  • fast 到达末尾时,slow 正好在中间

示例说明

text

复制代码
链表: 1 -> 2 -> 3 -> 2 -> 1

初始: slow=1, fast=1
第1次: slow=2, fast=3
第2次: slow=3, fast=1
结束: slow指向3(中间节点)

链表: 1 -> 2 -> 2 -> 1

初始: slow=1, fast=1
第1次: slow=2, fast=2
第2次: slow=2, fast=nil
结束: slow指向第二个2

3. 反转后半部分链表

go

复制代码
var prev *ListNode
cur := slow
for cur != nil {
    next := cur.Next
    cur.Next = prev
    prev = cur
    cur = next
}

反转过程详解

text

复制代码
假设链表后半部分: 3 -> 2 -> 1 -> nil
cur = 3, prev = nil

第1次循环:
  next = cur.Next = 2     // 保存下一个节点
  cur.Next = prev = nil   // 3 -> nil
  prev = cur = 3          // prev指向3
  cur = next = 2          // cur指向2

第2次循环:
  next = cur.Next = 1     // 保存下一个节点
  cur.Next = prev = 3     // 2 -> 3
  prev = cur = 2          // prev指向2
  cur = next = 1          // cur指向1

第3次循环:
  next = cur.Next = nil   // 保存下一个节点
  cur.Next = prev = 2     // 1 -> 2
  prev = cur = 1          // prev指向1
  cur = next = nil        // cur指向nil

结果: 1 -> 2 -> 3 -> nil

4. 比较前后两部分

go

复制代码
p1, p2 := head, prev
for p2 != nil {
    if p1.Val != p2.Val {
        return false
    }
    p1 = p1.Next
    p2 = p2.Next
}

比较逻辑

  • p1 从头节点开始(前半部分)

  • p2 从反转后的后半部分头节点开始

  • 逐个比较节点的值,只要有一个不相等就返回 false

为什么只比较到 p2 != nil

  • 因为后半部分长度 ≤ 前半部分

  • 奇数长度时,中间节点不需要比较

  • 偶数长度时,两部分长度相等

相关推荐
rit84324991 小时前
MFOCUSS算法MATLAB实现:稀疏信号重构
算法·matlab·重构
发疯幼稚鬼1 小时前
散列及其分离链接法
c语言·数据结构·算法·链表·散列表
Bdygsl1 小时前
数字图像处理总结 Day 1
人工智能·算法·计算机视觉
北郭guo1 小时前
垃圾回收底层原理【深入了解】
java·jvm·算法
小年糕是糕手1 小时前
【C++同步练习】C++入门
开发语言·数据结构·c++·算法·pdf·github·排序算法
报错小能手1 小时前
数据结构 链式队列
数据结构·算法
Octhexis1 小时前
LC191 位1的个数
算法
谷隐凡二1 小时前
Go语言实现Kubernetes主从架构模拟系统细节说明(姐妹篇)
架构·golang·kubernetes
LDG_AGI1 小时前
【推荐系统】深度学习训练框架(六):PyTorch DDP(DistributedDataParallel)数据并行分布式深度学习原理
人工智能·pytorch·分布式·python·深度学习·算法·spark