当前位置: 首页 > news >正文

链表算法---根本算法操作(go语言版)

一.链表基本结构定义

链表的基础结构是节点,Go 语言实现如下:

type ListNode struct {Val  intNext *ListNode
}

二.基础操作

创建链表(数组转链表)

func BuildList(nums []int) *ListNode {dummy := &ListNode{}cur := dummyfor _, v := range nums {cur.Next = &ListNode{Val: v}cur = cur.Next}return dummy.Next
}

遍历链表

func Traverse(head *ListNode) {for head != nil {fmt.Println(head.Val)head = head.Next}
}

插入节点


头插法(O(1)):
func InsertHead(head *ListNode, val int) *ListNode {return &ListNode{Val: val, Next: head}
}

尾插法(O(n)):
func InsertTail(head *ListNode, val int) *ListNode {if head == nil {return &ListNode{Val: val}}cur := headfor cur.Next != nil {cur = cur.Next}cur.Next = &ListNode{Val: val}return head
}

删除节点

func DeleteNode(head *ListNode, val int) *ListNode {dummy := &ListNode{Next: head}cur := dummyfor cur.Next != nil {if cur.Next.Val == val {cur.Next = cur.Next.Nextbreak}cur = cur.Next}return dummy.Next
}

三.常见算法题

1.反转链表

核心思想:指针反转 + 三指针遍历

链表无法随机访问,只能移动指针,因此反转时需要:

  • cur:当前节点

  • prev:新链表的头

  • next:提前保存 cur 的下一节点,防止链断掉

func ReverseList(head *ListNode) *ListNode {var prev *ListNodecur := headfor cur != nil {next := cur.Nextcur.Next = prevprev = curcur = next}return prev
}

2.快慢指针查找中间节点

核心思想:快慢指针

  • slow 每次走 1 步

  • fast 每次走 2 步

  • 当 fast 到尾部时,slow 刚好在中点

无需统计长度,O(n) 一次遍历完成。

func MiddleNode(head *ListNode) *ListNode {slow, fast := head, headfor fast != nil && fast.Next != nil {slow = slow.Nextfast = fast.Next.Next}return slow
}

3.判断链表是否有环

核心思想:Floyd 环检测算法

依旧使用快慢指针:

  • 若存在环,fast 迟早会追上 slow

  • 若不存在环,fast 会先遇到 nil

这是数学证明过的最优做法。

func HasCycle(head *ListNode) bool {slow, fast := head, headfor fast != nil && fast.Next != nil {slow = slow.Nextfast = fast.Next.Nextif slow == fast {return true}}return false
}

4.合并两个有序链表

核心思想:双指针 + 有序合并(类似归并排序 Merge 阶段)

两个链表都已经排序,因此:

  • 每次取两链表头部较小的节点

  • 挂到新链表后面

  • 移动取自的链表的指针

最终形成一个整体有序的链表。

func MergeTwoLists(l1, l2 *ListNode) *ListNode {dummy := &ListNode{}cur := dummyfor l1 != nil && l2 != nil {if l1.Val < l2.Val {cur.Next = l1l1 = l1.Next} else {cur.Next = l2l2 = l2.Next}cur = cur.Next}if l1 != nil {cur.Next = l1}if l2 != nil {cur.Next = l2}return dummy.Next
}

5.删除倒数第 N 个节点

核心思想:快慢指针 + 间隔法

为了一次遍历就定位倒数第 N:

  • 先让 fast 先走 N 步

  • 然后 slow 和 fast 一起走

  • 当 fast 到尾时,slow 刚好指向倒数第 N 的前一个节点

删除 slow 后的节点即可。

func RemoveNthFromEnd(head *ListNode, n int) *ListNode {dummy := &ListNode{Next: head}fast, slow := dummy, dummyfor i := 0; i < n; i++ {fast = fast.Next}for fast.Next != nil {fast = fast.Nextslow = slow.Next}slow.Next = slow.Next.Nextreturn dummy.Next
}

6.链表排序(归并排序)

核心思想:归并排序(Merge Sort on Linked List)

链表不适合快排(无法随机访问,pivot 划分难且不稳定)
最适合的是:归并排序

过程:

  1. 快慢指针找中点 → 分成左右两半

  2. 递归排序左右链表

  3. 合并两条有序链表(复用 MergeTwoLists)

时间:O(n log n)
空间:O(log n)(递归栈)

func SortList(head *ListNode) *ListNode {if head == nil || head.Next == nil {return head}slow, fast := head, head.Nextfor fast != nil && fast.Next != nil {slow = slow.Nextfast = fast.Next.Next}mid := slow.Nextslow.Next = nilleft := SortList(head)right := SortList(mid)return MergeTwoLists(left, right)
}

7.判圈算法

在力扣环形链表Ⅱ中,判断是否有环形链表,及找到入环点

1.判断是否有环形链表

方法一:用map的key唯一来判断当到同一个结点时就是环形链表同时返回入环点

方法二:用快,慢结点。快结点走两个结点,慢结点走一个,如果是环形链表肯定会相遇,如果不是在快结点走到尾返回

2.用方法二,利用判圈算法找到入环点

首先:假设慢结点走了b步,则快结点走了2b步,设环长为c,快结点比慢结点在相遇时在环中走了多走了k圈,则2b-b=kc。

其次:设从头结点到入环点的长度为a,则b-a就是慢结点在环中走的路程=kc-a。

最后:慢结点在走a步就能到达入环点(初始位置),并且头结点到达入环点的位置也是a。

8.链表相交

算法思路:双指针 A + B(不等长链表齐头并进法)
将两个链表 A 和 B 视为不同长度的路径:

  • A: a₁ → a₂ → a₃ → c₁ → c₂
  • B: b₁ → b₂ → c₁ → c₂

关键点

  • 使用两个指针 pApB,分别从链表 A 和 B 的头节点开始遍历。
  • 当指针走到链表末尾时,切换到另一条链表的头部继续遍历:
    • pA 的路径:A → B
    • pB 的路径:B → A

数学逻辑
由于 len(A) + len(B) = len(B) + len(A),若存在相交节点,两指针会在同一节点相遇(即交点)。若不相交,两指针最终会同时到达 null

效率分析

  • 时间复杂度:O(m + n),其中 m 和 n 分别为链表 A 和 B 的长度。
  • 空间复杂度:O(1),仅使用常数级额外空间。
func GetIntersectionNode(headA, headB *ListNode) *ListNode {if headA == nil || headB == nil {return nil}pA := headApB := headBfor pA != pB {if pA == nil {pA = headB   // A 走完,切换到 B} else {pA = pA.Next}if pB == nil {pB = headA   // B 走完,切换到 A} else {pB = pB.Next}}return pA  // 交点 or nil
}

四.进阶专题

  • K 个一组翻转链表
  • 链表加法(两数相加)
  • 复制带随机指针的链表
  • LRU 缓存(双向链表 + Hash)
  • 扁平化多级双向链表
http://www.jsqmd.com/news/353918/

相关文章:

  • ChatGPT电脑版下载与本地部署指南:从原理到实践
  • 从字节码视角看Arthas热部署:JVM内存中的代码魔术
  • MATLAB全桥或半桥LLC谐振DC/DC变换器仿真探索
  • 2026年草莓苗培育公司权威推荐:重庆果之王园艺有限公司,枇杷/桃/葡萄/樱桃等全系供应 - 品牌推荐官
  • 2026年地下水/气象/雨量/水质/水雨情监测站厂家推荐:三方源科技全系产品助力新基建 - 品牌推荐官
  • 实战解析:如何基于多多智能客服API构建高可用对话系统
  • 基于深度学习的西红柿成熟度检测系统 深度学习框架YOLOV8模型如何训练番茄西红柿成熟度检测数据集 智慧农业、农产品分拣、高校科研 毕业设计
  • Coqui TTS 代码下载与安装全指南:从源码编译到生产环境部署
  • 2026年梳理机分梳辊来图加工靠谱企业盘点,赶紧收藏 - 工业品牌热点
  • VisionPro 工业相机驱动连接(GigE 接口)结构化速记版
  • 2026年小型压路机厂家推荐:山东奔马工程机械,多功能/双钢轮/座驾式压路机等全系产品解析 - 品牌推荐官
  • AI+医疗产品客服智能体开发实战:从架构设计到生产环境避坑指南
  • 《ESP32-S3使用指南—IDF版 V1.6》第四章 开发环境搭建(下)
  • AI辅助开发实战:基于CosyVoice的智能语音标注系统设计与优化
  • 2026全自动/节能/高效加碱机厂家推荐:无锡市朗善机械设备科技,自动化加碱解决方案优选 - 品牌推荐官
  • 使用注入的方式修改unity游戏玩家名称
  • java+vue基于springboot框架的网上书店管理系统的设计与实现
  • ChatTTS HTTP接口调用指南:从原理到实战避坑
  • ChatTTS Python部署实战:从模型加载到生产环境避坑指南
  • Unity与鸿蒙深度整合:跨平台3D应用开发全流程解析
  • ChatGPT接口调用效率提升实战:从并发优化到错误处理
  • 2026冲刺用!专科生专属AI论文写作神器 —— 千笔·专业学术智能体
  • java+vue基于springboot框架的线上订餐骑手配送管理系统的设计与实现
  • 2026年必藏!8款亲测好用的AI论文初稿神器,学术党速码!
  • 交稿前一晚!8个降AI率工具测评:本科生必看的降AIGC神器推荐
  • 看完就会:全网爆红的一键生成论文工具 —— 千笔写作工具
  • 新唐NUC980开发实战:从零搭建Linux交叉编译环境与工具链配置
  • 软件工程人工智能方向毕业设计:从选题到落地的完整技术路径解析
  • UART协议中的停止位与校验位:如何通过波形分析避免数据丢失
  • 科研党收藏!千笔·专业学术智能体,研究生论文写作神器