链表

链表

内存空间是所有程序都可以访问的公共资源,在复杂的系统运行环境下,空闲的内存空间可能散落在内存各处。

有些时候,内存可能无法提供很大的连续空间。此时链表的优势就体现出来了。

链表是一种线性数据结构,每个元素都是一个节点对象,各个节点通过"引用"相连接。引用记录了下一个节点的内存地址,通过它可以从当前节点访问到下一个节点。

链表的设计使得节点可以分散存储在内存各处,它们的内存地址无须连续。

  • 链表的首个节点被称为"头节点",最后一个节点被称为"尾节点"。
  • 尾节点指向的是"空"。
  • 在 Go 语言中,"引用"指定是"指针"。

链表节点除了包含值,还需额外保存一个指针。因此在相同数据量下,链表比数组占用更多的内存空间

go 复制代码
/* 链表节点结构体 */
type ListNode struct {
    Val  int       // 节点值
    Next *ListNode // 指向下一节点的指针
}
​
// NewListNode 构造函数,创建一个新的链表
func NewListNode(val int) *ListNode {
    return &ListNode{
        Val:  val,
        Next: nil,
    }
}

链表常用操作

初始化链表

建立链表分为两步:

第一步是初始化各个节点对象

第二步是构建节点之间的引用关系

初始化完成后,就可以从链表的头节点出发,通过指针指向 next 依次访问所有节点。

go 复制代码
/* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */
// 初始化各个节点
n0 := NewListNode(1)
n1 := NewListNode(3)
n2 := NewListNode(2)
n3 := NewListNode(5)
n4 := NewListNode(4)
// 构建节点之间的引用
n0.Next = n1
n1.Next = n2
n2.Next = n3
n3.Next = n4

插入节点

链表中插入节点非常容易。则只需改变两个节点引用(指针)即可,时间复杂度为O(1) 。

go 复制代码
/* 在链表的节点 n0 之后插入节点 P */
func insertNode(n0 *ListNode, P *ListNode) {
    n1 := n0.Next
    P.Next = n1
    n0.Next = P
}

删除节点

链表中删除节点也非常方便,只需改变一个节点的引用(指针)即可

go 复制代码
/* 删除链表的节点 n0 之后的首个节点 */
func removeItem(n0 *ListNode) {
    if n0.Next == nil {
        return
    }
    // n0 -> P -> n1
    P := n0.Next
    n1 := P.Next
    n0.Next = n1
}

访问节点

链表中访问节点的效率较低,需要从头节点出发依次遍历

go 复制代码
/* 访问链表中索引为 index 的节点 */
func access(head *ListNode, index int) *ListNode {
    for i := 0; i < index; i++ {
        if head == nil {
            return nil
        }
        head = head.Next
    }
    return head
}

查找节点

遍历链表,查找其中值为 target 的节点,输出该节点在链表中的索引。此过程也属于线性查找。

go 复制代码
/* 在链表中查找值为 target 的首个节点 */
func findNode(head *ListNode, target int) int {
    index := 0
    for head != nil {
        if head.Val == target {
            return index
        }
        head = head.Next
        index++
    }
    return -1
}

数组和链表

数组 链表
存储方式 连续内存空间 分散内存空间
容量扩展 长度不可变 可灵活扩展
内存效率 元素占用内存少、但可能浪费空间 元素占用内存多
访问元素 O(1) O(n)
添加元素 O(n) O(1)
删除元素 O(n) O(1)

常见链表类型

  • 单向链表 :即前面介绍的普通链表。单向链表的节点包含值和指向下一节点的引用两项数据。我们将首个节点称为头节点,将最后一个节点称为尾节点,尾节点指向空 None
  • 环形链表:如果我们令单向链表的尾节点指向头节点(首尾相接),则得到一个环形链表。在环形链表中,任意节点都可以视作头节点。
  • 双向链表:与单向链表相比,双向链表记录了两个方向的引用。双向链表的节点定义同时包含指向后继节点(下一个节点)和前驱节点(上一个节点)的引用(指针)。相较于单向链表,双向链表更具灵活性,可以朝两个方向遍历链表,但相应地也需要占用更多的内存空间。
相关推荐
WWZZ202529 分钟前
快速上手大模型:机器学习3(多元线性回归及梯度、向量化、正规方程)
人工智能·算法·机器学习·机器人·slam·具身感知
东方佑1 小时前
从字符串中提取重复子串的Python算法解析
windows·python·算法
西阳未落2 小时前
LeetCode——二分(进阶)
算法·leetcode·职场和发展
通信小呆呆2 小时前
以矩阵视角统一理解:外积、Kronecker 积与 Khatri–Rao 积(含MATLAB可视化)
线性代数·算法·matlab·矩阵·信号处理
Dfreedom.2 小时前
一文掌握Python四大核心数据结构:变量、结构体、类与枚举
开发语言·数据结构·python·变量·数据类型
CoderCodingNo3 小时前
【GESP】C++四级真题 luogu-B4068 [GESP202412 四级] Recamán
开发语言·c++·算法
一个不知名程序员www3 小时前
算法学习入门---双指针(C++)
c++·算法
Shilong Wang4 小时前
MLE, MAP, Full Bayes
人工智能·算法·机器学习
Theodore_10224 小时前
机器学习(6)特征工程与多项式回归
深度学习·算法·机器学习·数据分析·多项式回归
知花实央l4 小时前
【算法与数据结构】拓扑排序实战(栈+邻接表+环判断,附可运行代码)
数据结构·算法