1. 链表基础概念与核心操作
链表作为一种基础数据结构,在算法面试和实际开发中占据着重要地位。与数组不同,链表通过指针将零散的内存块串联起来,每个节点包含数据域和指针域。这种非连续存储的特性,使得链表在插入和删除操作上具有O(1)的时间复杂度优势。
1.1 链表的基本类型
常见的链表类型包括:
- 单链表:每个节点只有一个next指针指向下一个节点
- 双链表:节点包含prev和next两个指针,可双向遍历
- 循环链表:尾节点指向头节点形成环状结构
- 静态链表:用数组模拟链表结构,常见于嵌入式等内存受限场景
提示:在C++中,链表节点通常用结构体定义,包含数据成员和指针成员。Python则可以用类来实现,通过__init__方法初始化节点属性。
1.2 链表的核心操作
链表的基本操作包括:
- 遍历:从头节点开始,依次访问每个节点
- 插入:在指定位置插入新节点,调整相邻节点的指针
- 删除:移除指定节点,并正确连接其前后节点
- 查找:按值或位置查找特定节点
- 逆序:改变节点间的指针方向实现链表反转
以单链表插入为例,关键代码逻辑如下:
python复制def insert_node(head, position, value):
new_node = ListNode(value)
if position == 0: # 头插
new_node.next = head
return new_node
current = head
for _ in range(position-1):
if not current:
raise IndexError("Position out of range")
current = current.next
new_node.next = current.next
current.next = new_node
return head
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 链表与其它线性结构的对比
2.1 链表 vs 数组/顺序表
| 特性 | 数组/顺序表 | 链表 |
|---|---|---|
| 内存布局 | 连续内存 | 非连续内存 |
| 随机访问 | O(1) | O(n) |
| 插入删除 | O(n) | O(1) |
| 空间利用率 | 高(无额外指针开销) | 低(有指针存储开销) |
| 缓存友好性 | 好 | 差 |
2.2 链表在数据结构体系中的位置
链表与栈、队列的关系:
- 栈可以用链表实现(头插头删)
- 队列可以用链表实现(头删尾插)
- 双端队列则需要双链表支持
在实际工程中,链表常用于:
- 内存管理中的空闲内存块组织
- 文件系统的目录结构表示
- 图的邻接表存储方式
- 浏览器历史记录管理等场景
3. 链表常见面试题解析
3.1 链表逆序问题
链表逆序是面试最高频的问题之一,有递归和迭代两种解法。迭代法需要维护prev、current、next三个指针:
python复制def reverse_list(head):
prev = None
current = head
while current:
next_node = current.next # 暂存下一个节点
current.next = prev # 反转指针
prev = current # 前移prev
current = next_node # 前移current
return prev
递归解法虽然简洁但空间复杂度为O(n):
python复制def reverse_list_recursive(head):
if not head or not head.next:
return head
new_head = reverse_list_recursive(head.next)
head.next.next = head
head.next = None
return new_head
3.2 链表环检测
判断链表是否有环可以使用快慢指针法(Floyd判圈算法):
python复制def has_cycle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False
进阶问题:找出环的入口节点。当快慢指针相遇后,将其中一个指针移回head,然后同速前进,再次相遇点即为环入口。
3.3 链表合并与分割
合并两个有序链表是基础但重要的问题:
python复制def merge_two_lists(l1, l2):
dummy = ListNode(0)
current = dummy
while l1 and l2:
if l1.val < l2.val:
current.next = l1
l1 = l1.next
else:
current.next = l2
l2 = l2.next
current = current.next
current.next = l1 if l1 else l2
return dummy.next
链表分割问题(如以某个值x将链表分为小于x和大于等于x两部分)需要注意保持原始相对顺序,通常需要创建两个子链表再合并。
4. 链表高级应用与优化
4.1 静态链表的实现
静态链表使用数组模拟链表结构,适用于不支持指针或动态内存分配的环境。其核心思想是用数组下标代替指针:
c复制#define MAX_SIZE 100
typedef struct {
int data;
int next; // 存储下一个节点的数组下标
} StaticNode;
StaticNode pool[MAX_SIZE];
int head = -1; // 头节点索引
int free_list = 0; // 空闲节点链表头
// 初始化时将所有节点串成空闲链表
void init_pool() {
for (int i = 0; i < MAX_SIZE-1; i++) {
pool[i].next = i+1;
}
pool[MAX_SIZE-1].next = -1;
}
4.2 链表去重算法
有序链表去重相对简单,只需比较相邻节点:
python复制def delete_duplicates(head):
current = head
while current and current.next:
if current.val == current.next.val:
current.next = current.next.next
else:
current = current.next
return head
无序链表去重则需要借助哈希表记录已出现过的值:
python复制def delete_duplicates_unsorted(head):
seen = set()
dummy = ListNode(0, head)
prev = dummy
while head:
if head.val in seen:
prev.next = head.next
else:
seen.add(head.val)
prev = prev.next
head = head.next
return dummy.next
4.3 链表与树结构的转换
某些情况下需要将链表转换为二叉树(如有序链表转二叉搜索树),通常采用分治+快慢指针找中点的方法:
python复制def sorted_list_to_bst(head):
if not head:
return None
if not head.next:
return TreeNode(head.val)
# 快慢指针找中点
slow, fast = head, head.next.next
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# 分割链表
mid = slow.next
slow.next = None
# 递归构建树
root = TreeNode(mid.val)
root.left = sorted_list_to_bst(head)
root.right = sorted_list_to_bst(mid.next)
return root
5. 链表操作实战技巧
5.1 虚拟头节点技巧
在处理链表问题时,引入dummy节点可以简化边界条件处理:
python复制def remove_elements(head, val):
dummy = ListNode(0, head)
prev = dummy
while head:
if head.val == val:
prev.next = head.next
else:
prev = prev.next
head = head.next
return dummy.next
5.2 多指针协同策略
复杂链表问题往往需要多个指针协同工作。例如,删除倒数第n个节点:
python复制def remove_nth_from_end(head, n):
dummy = ListNode(0, head)
fast = slow = dummy
for _ in range(n+1):
fast = fast.next
while fast:
fast = fast.next
slow = slow.next
slow.next = slow.next.next
return dummy.next
5.3 链表调试与可视化
调试链表问题时,可以添加打印链表内容的辅助函数:
python复制def print_list(head):
res = []
while head:
res.append(str(head.val))
head = head.next
print("->".join(res))
对于复杂问题,建议在纸上画出链表结构和指针变化过程,这对理解指针操作非常有帮助。
6. 链表在工程中的应用实例
6.1 LRU缓存实现
LRU缓存淘汰算法通常使用哈希表+双链表实现:
python复制class LRUCache:
class DLinkedNode:
def __init__(self, key=0, value=0):
self.key = key
self.value = value
self.prev = None
self.next = None
def __init__(self, capacity: int):
self.capacity = capacity
self.size = 0
self.cache = {}
self.head = self.DLinkedNode()
self.tail = self.DLinkedNode()
self.head.next = self.tail
self.tail.prev = self.head
def get(self, key: int) -> int:
if key not in self.cache:
return -1
node = self.cache[key]
self._move_to_head(node)
return node.value
def put(self, key: int, value: int) -> None:
if key in self.cache:
node = self.cache[key]
node.value = value
self._move_to_head(node)
else:
node = self.DLinkedNode(key, value)
self.cache[key] = node
self._add_to_head(node)
self.size += 1
if self.size > self.capacity:
removed = self._remove_tail()
del self.cache[removed.key]
self.size -= 1
def _add_to_head(self, node):
node.prev = self.head
node.next = self.head.next
self.head.next.prev = node
self.head.next = node
def _remove_node(self, node):
node.prev.next = node.next
node.next.prev = node.prev
def _move_to_head(self, node):
self._remove_node(node)
self._add_to_head(node)
def _remove_tail(self):
node = self.tail.prev
self._remove_node(node)
return node
6.2 多项式运算
链表非常适合表示稀疏多项式,每个节点存储系数和指数:
python复制class PolyNode:
def __init__(self, coeff=0, power=0, next=None):
self.coeff = coeff
self.power = power
self.next = next
def add_poly(poly1, poly2):
dummy = PolyNode()
current = dummy
while poly1 and poly2:
if poly1.power > poly2.power:
current.next = PolyNode(poly1.coeff, poly1.power)
poly1 = poly1.next
elif poly1.power < poly2.power:
current.next = PolyNode(poly2.coeff, poly2.power)
poly2 = poly2.next
else:
coeff = poly1.coeff + poly2.coeff
if coeff != 0:
current.next = PolyNode(coeff, poly1.power)
poly1 = poly1.next
poly2 = poly2.next
current = current.next if current.next else current
current.next = poly1 if poly1 else poly2
return dummy.next
7. 链表问题进阶训练
7.1 复杂链表复制
带有随机指针的链表复制问题需要巧妙的方法:
python复制class Node:
def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
self.val = int(x)
self.next = next
self.random = random
def copyRandomList(head):
if not head:
return None
# 第一步:在每个原节点后面创建一个新节点
current = head
while current:
new_node = Node(current.val)
new_node.next = current.next
current.next = new_node
current = new_node.next
# 第二步:设置新节点的random指针
current = head
while current:
if current.random:
current.next.random = current.random.next
current = current.next.next
# 第三步:拆分两个链表
current = head
new_head = head.next
while current:
temp = current.next
current.next = temp.next
if temp.next:
temp.next = temp.next.next
current = current.next
return new_head
7.2 链表排序
链表排序通常使用归并排序,因其符合链表的特性:
python复制def sortList(head):
if not head or not head.next:
return head
# 快慢指针找中点
slow, fast = head, head.next
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# 分割链表
mid = slow.next
slow.next = None
# 递归排序
left = sortList(head)
right = sortList(mid)
# 合并有序链表
dummy = ListNode(0)
current = dummy
while left and right:
if left.val < right.val:
current.next = left
left = left.next
else:
current.next = right
right = right.next
current = current.next
current.next = left if left else right
return dummy.next
7.3 链表相交与环问题进阶
判断两个链表是否相交,可以先用快慢指针判断各自是否有环,然后分情况处理:
- 都无环:计算长度差,长链表先走差值步,然后同步前进比较节点
- 一个有环一个无环:不可能相交
- 都有环:先找到各自的环入口,如果相同则相交在环外,否则可能在环内相交
python复制def getIntersectionNode(headA, headB):
def get_loop_entry(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
break
if not fast or not fast.next:
return None
slow = head
while slow != fast:
slow = slow.next
fast = fast.next
return slow
entryA = get_loop_entry(headA)
entryB = get_loop_entry(headB)
# 都无环的情况
if not entryA and not entryB:
lenA = lenB = 0
pA, pB = headA, headB
while pA:
lenA += 1
pA = pA.next
while pB:
lenB += 1
pB = pB.next
pA, pB = headA, headB
if lenA > lenB:
for _ in range(lenA - lenB):
pA = pA.next
else:
for _ in range(lenB - lenA):
pB = pB.next
while pA != pB:
pA = pA.next
pB = pB.next
return pA
# 一个有环一个无环,不可能相交
elif (entryA and not entryB) or (not entryA and entryB):
return None
# 都有环的情况
else:
if entryA == entryB: # 相交在环外
lenA = lenB = 0
pA, pB = headA, headB
while pA != entryA:
lenA += 1
pA = pA.next
while pB != entryB:
lenB += 1
pB = pB.next
pA, pB = headA, headB
if lenA > lenB:
for _ in range(lenA - lenB):
pA = pA.next
else:
for _ in range(lenB - lenA):
pB = pB.next
while pA != pB:
pA = pA.next
pB = pB.next
return pA
else: # 检查是否在环内相交
p = entryA.next
while p != entryA:
if p == entryB:
return entryA # 或者entryB都可以
p = p.next
return None
