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

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

一.链表基本结构定义

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

type ListNode struct {  Val  int  Next *ListNode  
}  

二.基础操作

创建链表(数组转链表)

func BuildList(nums []int) *ListNode {  dummy := &ListNode{}  cur := dummy  for _, 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 := head  for 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 := dummy  for cur.Next != nil {  if cur.Next.Val == val {  cur.Next = cur.Next.Next  break  }  cur = cur.Next  }  return dummy.Next  
}  

三.常见算法题

1.反转链表

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

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

  • cur:当前节点

  • prev:新链表的头

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

func ReverseList(head *ListNode) *ListNode {  var prev *ListNode  cur := head  for cur != nil {  next := cur.Next  cur.Next = prev  prev = cur  cur = next  }  return prev  
}  

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

核心思想:快慢指针

  • slow 每次走 1 步

  • fast 每次走 2 步

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

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

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

3.判断链表是否有环

核心思想:Floyd 环检测算法

依旧使用快慢指针:

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

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

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

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

4.合并两个有序链表

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

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

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

  • 挂到新链表后面

  • 移动取自的链表的指针

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

func MergeTwoLists(l1, l2 *ListNode) *ListNode {  dummy := &ListNode{}  cur := dummy  for l1 != nil && l2 != nil {  if l1.Val < l2.Val {  cur.Next = l1  l1 = l1.Next  } else {  cur.Next = l2  l2 = 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, dummy  for i := 0; i < n; i++ {  fast = fast.Next  }  for fast.Next != nil {  fast = fast.Next  slow = slow.Next  }  slow.Next = slow.Next.Next  return 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.Next  for fast != nil && fast.Next != nil {  slow = slow.Next  fast = fast.Next.Next  }  mid := slow.Next  slow.Next = nil  left := 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.dtcms.com/a/589221.html

相关文章:

  • 【开题答辩全过程】以 基于SpringBoot房源出租信息系统的设计与实现为例,包含答辩的问题和答案
  • 解锁MIME:Qt中的数据传输密码
  • 【Go 与云原生】让一个 Go 项目脱离原生的操作系统——我们开始使用 Docker 制造云容器进行时
  • 大语言模型学习之路(一)
  • 网页设计教程网页设计培训福州关键词优化平台
  • GPIO中断编程
  • 手机和pc合一的网站云南集优科技网站
  • 14.大语言模型微调语料构建
  • Docker 40个自动化管理脚本
  • 国外html5网站模版网站建设代码流程
  • 基于多智能体技术的码头车辆最快行驶路径方案重构
  • 网站备案空壳制作微信网站
  • Java119 反射使用
  • 基于springboot付费问答系统【带源码和文档】
  • 焦作官网网站推广工具如何制作一个报名微信小程序
  • Babylon.js中PBRMetallicRoughnessMaterial材质系统深度解析:从基础到工程实践
  • Linuxgit入门设计模式(常考点)
  • Arbess CICD实践(3) - 使用Arbess+GitPuk+sourcefare实现Node.js项目自动化部署+代码扫描
  • 设计网站的流程网站如何做地推
  • 力扣154. 寻找旋转排序数组中的最小值 II
  • React构建工具升级
  • @Transactional注解的切点匹配
  • 建设网站 请示 报告淘宝网站制作公司哪家好
  • leetcode1770.执行乘法运算的最大分数
  • 本溪市城乡住房建设厅网站国外做网站侵权
  • 虚拟化入门笔记
  • 物联网设备运维中的自适应硬件老化监测与寿命预测技术
  • dede网站建设360商城官网
  • 【云运维】LNMP 架构部署与应用
  • 【最长连续序列】