LinkedList
单向链表数据结构,元素通过指针连接。
函数签名
typescript
function createLinkedList<T>(): LinkedList<T>
interface LinkedList<T> {
prepend(value: T): void
append(value: T): void
insertAt(index: number, value: T): boolean
removeAt(index: number): T | undefined
indexOf(value: T): number
get(index: number): T | undefined
size(): number
isEmpty(): boolean
clear(): void
toArray(): T[]
reverse(): void
}
interface ListNode<T> {
value: T
next: ListNode<T> | null
}API 说明
prepend(value: T): void
在链表头部添加元素。
参数:
value: 要添加的元素
时间复杂度:O(1)
append(value: T): void
在链表尾部添加元素。
参数:
value: 要添加的元素
时间复杂度:O(n)
insertAt(index: number, value: T): boolean
在指定位置插入元素。
参数:
index: 插入位置(0-based)value: 要插入的元素
返回值:
- 插入成功返回
true,索引无效返回false
时间复杂度:O(n)
removeAt(index: number): T | undefined
移除指定位置的元素。
参数:
index: 要移除的位置(0-based)
返回值:
- 被移除的元素,索引无效返回
undefined
时间复杂度:O(n)
indexOf(value: T): number
查找元素的索引。
参数:
value: 要查找的元素
返回值:
- 元素的索引,未找到返回
-1
时间复杂度:O(n)
get(index: number): T | undefined
获取指定位置的元素。
参数:
index: 位置(0-based)
返回值:
- 该位置的元素,索引无效返回
undefined
时间复杂度:O(n)
size(): number
获取链表长度。
返回值:
- 链表中元素的数量
时间复杂度:O(1)
isEmpty(): boolean
检查链表是否为空。
返回值:
- 链表为空返回
true,否则返回false
时间复杂度:O(1)
clear(): void
清空链表。
时间复杂度:O(1)
toArray(): T[]
转换为数组。
返回值:
- 包含所有元素的数组
时间复杂度:O(n)
reverse(): void
反转链表。
时间复杂度:O(n)
使用示例
typescript
import { createLinkedList } from '@shared/functions/data-structures/LinkedList'
// 创建链表
const list = createLinkedList<string>()
// 添加元素
list.append('A')
list.append('B')
list.append('C')
list.prepend('Z') // 头部添加
console.log(list.toArray()) // ['Z', 'A', 'B', 'C']
// 插入元素
list.insertAt(2, 'X') // 在索引 2 处插入
console.log(list.toArray()) // ['Z', 'A', 'X', 'B', 'C']
// 查找元素
console.log(list.indexOf('X')) // 2
// 获取元素
console.log(list.get(2)) // 'X'
// 移除元素
console.log(list.removeAt(2)) // 'X'
console.log(list.toArray()) // ['Z', 'A', 'B', 'C']
// 反转链表
list.reverse()
console.log(list.toArray()) // ['C', 'B', 'A', 'Z']
// 获取大小
console.log(list.size()) // 4
// 清空链表
list.clear()
console.log(list.isEmpty()) // true工作原理
链表是一种线性数据结构,由一系列节点组成,每个节点包含:
- 数据部分:存储元素值
- 指针部分:指向下一个节点
单向链表特点:
- 不需要连续内存空间
- 动态大小,可以方便地添加和删除元素
- 头部插入/删除效率高 O(1)
- 随机访问效率低 O(n)
实现细节:
- 维护
head指针指向第一个节点 - 维护
length计数器记录元素数量 - 遍历链表需要从头节点开始逐个访问
应用场景
- 动态内存分配:操作系统中的内存管理
- 实现栈和队列:链表可以高效实现这些数据结构
- 音乐播放列表:歌曲的顺序播放和循环
- 浏览器历史:前进和后退功能
- 图的邻接表表示:图论算法
- LRU 缓存:结合哈希表实现
链表 vs 数组
| 特性 | 链表 | 数组 |
|---|---|---|
| 内存分配 | 动态,不连续 | 静态或动态,连续 |
| 随机访问 | O(n) | O(1) |
| 头部插入/删除 | O(1) | O(n) |
| 尾部插入/删除 | O(n) | O(1) |
| 空间效率 | 额外指针开销 | 无额外开销 |
经典算法
检测链表中的环
typescript
function hasCycle<T>(list: LinkedList<T>): boolean {
// 使用快慢指针
let slow: ListNode<T> | null = head
let fast: ListNode<T> | null = head
while (fast && fast.next) {
slow = slow!.next
fast = fast.next.next
if (slow === fast) {
return true // 有环
}
}
return false // 无环
}找到链表的中间节点
typescript
function findMiddle<T>(list: LinkedList<T>): number {
// 使用快慢指针
let slow = 0
let fast = 0
const size = list.size()
while (fast < size && fast + 1 < size) {
slow++
fast += 2
}
return slow
}合并两个有序链表
typescript
function mergeSortedLists<T>(
list1: LinkedList<T>,
list2: LinkedList<T>,
compare: (a: T, b: T) => number
): LinkedList<T> {
const result = createLinkedList<T>()
const arr1 = list1.toArray()
const arr2 = list2.toArray()
let i = 0, j = 0
while (i < arr1.length && j < arr2.length) {
if (compare(arr1[i], arr2[j]) <= 0) {
result.append(arr1[i++])
} else {
result.append(arr2[j++])
}
}
while (i < arr1.length) result.append(arr1[i++])
while (j < arr2.length) result.append(arr2[j++])
return result
}性能特点
| 操作 | 时间复杂度 | 说明 |
|---|---|---|
| 头部添加 | O(1) | 直接修改 head 指针 |
| 尾部添加 | O(n) | 需要遍历到末尾 |
| 指定位置插入 | O(n) | 需要遍历到该位置 |
| 删除 | O(n) | 需要遍历找到位置 |
| 查找 | O(n) | 需要遍历链表 |
| 反转 | O(n) | 需要遍历整个链表 |