一、队列
1、概念与特性
-
定义:队列是一种先进先出(FIFO,First In First Out)的线性数据结构。
-
特性:
-
插入(Enqueue) 操作在队尾(Tail)进行;
-
删除(Dequeue) 操作在队首(Head)进行;
-
任意时刻,最先进入队列的元素总是最先被移除。
-
2、基本操作及复杂度
操作 | 含义 | 时间复杂度(均摊) |
---|---|---|
enqueue(x) | 在队尾插入元素 x | O(1) |
dequeue() | 移除并返回队首元素 | O(1) |
peek() | 返回队首元素(不移除) | O(1) |
is_empty() | 检查队列是否为空 | O(1) |
size() | 返回当前队列元素个数 | O(1) |
3、python演示
from collections import deque
class Queue:
def __init__(self):
"""初始化一个空队列"""
self._data = deque()
def enqueue(self, x):
"""在队尾插入元素 x —— O(1)"""
self._data.append(x)
def dequeue(self):
"""移除并返回队首元素 —— O(1)
Raises:
IndexError: 队列为空时无法出队
"""
if self.is_empty():
raise IndexError("dequeue from empty queue")
return self._data.popleft()
def peek(self):
"""返回队首元素但不移除 —— O(1)
Raises:
IndexError: 队列为空时无法访问
"""
if self.is_empty():
raise IndexError("peek from empty queue")
return self._data[0]
def is_empty(self):
"""检查队列是否为空 —— O(1)"""
return not self._data
def size(self):
"""返回当前队列元素个数 —— O(1)"""
return len(self._data)
4、典型应用
-
滑动窗口算法
-
如“最大/最小滑动窗口”问题,用双端队列在 O(n)内维护当前窗口极值。
-
-
回文检查
-
将字符串两端同时
pop
,快速判断回文。
-
-
任务优先级调整
-
可在队首插入紧急任务,也可在队尾插入普通任务。
-
-
实现栈或队列
-
双端队列即为两种数据结构的超集。
-
-
图算法变体
-
如 0–1 BFS(边权为 0/1 的最短路),对权 0 边
append_front
,对权 1 边append_back
,即可达 O(V+E)时间复杂度。
-
二、双端队列
1、概念与特性
-
定义:双端队列(Deque,Double‐Ended Queue)是一种既可以在队首(Front)也可以在队尾(Back)进行插入和删除操作的线性数据结构。
-
特性:
-
既可做先进先出(FIFO),也可做后进先出(LIFO);
-
支持在两端均为 O(1)的插入与删除;
-
灵活度高,常作为栈和队列的通用底层结构。
-
2、基本操作及复杂度
操作 | 含义 | 时间复杂度 |
---|---|---|
append_back(x) | 在队尾插入元素 xxx | O(1) |
append_front(x) | 在队首插入元素 xxx | O(1) |
pop_back() | 移除并返回队尾元素 | O(1) |
pop_front() | 移除并返回队首元素 | O(1) |
peek_back() /back() | 返回队尾元素(不移除) | O(1) |
peek_front() /front() | 返回队首元素(不移除) | O(1) |
is_empty() | 检查是否为空 | O(1) |
size() | 返回当前元素个数 | O(1) |
3、python演示
from collections import deque
class Deque:
def __init__(self):
"""初始化双端队列"""
self._data = deque()
def append_back(self, x):
"""在队尾插入元素 x —— O(1)"""
self._data.append(x)
def append_front(self, x):
"""在队首插入元素 x —— O(1)"""
self._data.appendleft(x)
def pop_back(self):
"""移除并返回队尾元素 —— O(1)"""
if self.is_empty():
raise IndexError("pop_back from empty deque")
return self._data.pop()
def pop_front(self):
"""移除并返回队首元素 —— O(1)"""
if self.is_empty():
raise IndexError("pop_front from empty deque")
return self._data.popleft()
def peek_back(self):
"""返回队尾元素但不移除 —— O(1)"""
if self.is_empty():
raise IndexError("peek_back from empty deque")
return self._data[-1]
def peek_front(self):
"""返回队首元素但不移除 —— O(1)"""
if self.is_empty():
raise IndexError("peek_front from empty deque")
return self._data[0]
def is_empty(self):
"""检查是否为空 —— O(1)"""
return not self._data
def size(self):
"""返回当前元素个数 —— O(1)"""
return len(self._data)
4、典型应用
-
Dijkstra 最短路径算法
-
每次选取当前“最短”未确定路径的顶点。
-
-
事件驱动模拟
-
按事件发生时间(优先级)顺序调度。
-
-
A 搜索算法*
-
按启发式代价 f=g+h排序节点。
-
-
操作系统调度
-
依据进程优先级或到达时间分配 CPU 时间片。
-
-
合并多路有序序列
-
利用优先队列高效选择当前最小元素。
-
三、优先队列
1、概念与特性
-
定义:优先队列是一种特殊的抽象数据类型,每次出队操作都返回“优先级”最高(或最低)的元素,而不一定是最先进入的元素。
-
常见实现:
-
二叉堆(Binary Heap,最常用)
-
斐波那契堆(Fibonacci Heap)
-
左偏树(Leftist Tree)等
-
-
特性:
-
插入新元素后,都能保持快速找到并移除“最优”元素;
-
平均或摊销时间复杂度常为 O(logn)
-
2、基本操作及复杂度
操作 | 含义 | 时间复杂度 |
---|---|---|
push(item, priority) | 插入元素 item 并指定其优先级 priority | O(logn) |
pop() | 移除并返回“最优”元素(最高或最低优先级) | O(logn) |
peek() /top() | 查看“最优”元素但不移除 | O(1) |
is_empty() | 检查优先队列是否为空 | O(1) |
size() | 返回当前元素个数 | O(1) |
3、python演示
import heapq
class PriorityQueue:
def __init__(self):
"""初始化一个空的最小优先队列"""
self._heap = []
self._count = 0 # 用于处理相同优先级时的入队顺序
def push(self, item, priority):
"""
插入元素 item,优先级越小表示越“高优先级” —— O(log n)
如果希望“优先级越大越先出”,可将 priority 取负值。
"""
# 使用 count 保证当 priority 相同时,先插入的先出
heapq.heappush(self._heap, (priority, self._count, item))
self._count += 1
def pop(self):
"""
移除并返回优先级最高(最小 priority)的元素 —— O(log n)
Raises:
IndexError: 队列为空时无法弹出
"""
if self.is_empty():
raise IndexError("pop from empty priority queue")
_, _, item = heapq.heappop(self._heap)
return item
def peek(self):
"""
查看但不移除优先级最高的元素 —— O(1)
Raises:
IndexError: 队列为空时无法查看
"""
if self.is_empty():
raise IndexError("peek from empty priority queue")
return self._heap[0][2]
def is_empty(self):
"""检查是否为空 —— O(1)"""
return not self._heap
def size(self):
"""返回当前元素个数 —— O(1)"""
return len(self._heap)
4、典型应用
-
优先队列底层:所有需要“快速取最小/最大”的场合(任务调度、事件模拟)
-
图算法:
-
Dijkstra 最短路径(每次取最小距离顶点)
-
Prim 最小生成树
-
-
贪心算法:合并多路有序列表、求中位数维护(双堆法)
-
滑动窗口中值:维护两堆实现实时中位数
这里heap为堆数据结构,后面会简单介绍,详细介绍https://blog.csdn.net/qq_43191011/article/details/148304655?spm=1001.2014.3001.5502
四、堆
1、概念与特性
-
定义
堆是一种完全二叉树结构,分为最大堆(Max-Heap)和最小堆(Min-Heap):-
最大堆:每个父节点的值 ≥ 其子节点的值;
-
最小堆:每个父节点的值 ≤ 其子节点的值。
-
-
存储
利用数组即可高效存储:若根节点在索引 0,则对于节点索引i
-
左子节点索引 =
2*i + 1
-
右子节点索引 =
2*i + 2
-
父节点索引 =
(i - 1) // 2
-
-
特点
-
结构性:始终保持“完全二叉树”形态,无空洞;
-
堆序性:根节点即全树最小(或最大);
-
高效构建:可在 O(n)时间内由无序数组“heapify”成堆。
-
2、核心操作及时间复杂度
操作 | 含义 | 时间复杂度 |
---|---|---|
push(x) / insert(x) | 插入元素 xxx,并“上浮”恢复堆性质 | O(logn) |
pop() / extract() | 弹出并返回堆顶元素(最小或最大),并“下沉” | O(logn) |
peek() / top() | 查看堆顶元素但不移除 | O(1) |
build_heap(A) | 将数组 A原地转堆(heapify) | O(n) |
size() | 返回当前元素个数 | O(1) |
is_empty() | 检查是否为空 | O(1) |
-
上浮 (Sift-up):插入元素后,从叶子向上交换到正确位置。
-
下沉 (Sift-down):弹出堆顶后,用末尾元素顶替,从根向下交换到正确位置。
3、python演示
import heapq
# 创建空最小堆
min_heap = []
# 插入元素
for x in [5, 3, 8, 1, 6]:
heapq.heappush(min_heap, x) # O(log n)
# 查看堆顶
print("Peek:", min_heap[0]) # 1
# 弹出元素按升序
while min_heap:
print(heapq.heappop(min_heap), end=" ")
# 输出: 1 3 5 6 8
max_heap = []
for x in [5, 3, 8, 1, 6]:
heapq.heappush(max_heap, -x)
print("Peek (max):", -max_heap[0]) # 8
while max_heap:
print(-heapq.heappop(max_heap), end=" ")
# 输出: 8 6 5 3 1
4、典型应用
-
优先队列底层:
-
事件模拟系统(按时间优先);
-
操作系统调度。
-
-
图算法:
-
Dijkstra 最短路径(取最小距离顶点);
-
Prim 最小生成树。
-
-
贪心合并:
-
合并 kkk 路有序链表/数组;
-
实时中位数维护(双堆法)。
-
-
滑动窗口中值:
-
同时维护两堆,快速更新中位数。
-
手动建堆的代码详见https://blog.csdn.net/qq_43191011/article/details/148304655?spm=1001.2014.3001.5502
五、链表(基础)
1、概念与特性
-
定义:链表由一系列节点(Node)通过指针(或引用)串联而成。每个节点一般包含两部分信息
-
数据域(data):存放实际数据
-
指针域(next/prev):指向下一个(或前一个)节点
-
-
类型:
-
单向链表(Singly Linked List):每个节点仅有
next
指针 -
双向链表(Doubly Linked List):每个节点有
prev
和next
两个指针 -
循环链表(Circular Linked List):尾节点指向头节点,形成环状
-
-
特点
-
动态大小:节点可以随时增删,内存利用灵活
-
插入/删除快:若已知位置,仅需修改少量指针,操作为 O(1)
-
随机访问慢:访问第 k 个节点需从头遍历,时间为 O(k)
-
2、基本操作及复杂度
操作 | 含义 | 时间复杂度 |
---|---|---|
prepend(x) | 在链表头插入元素 xxx | O(1) |
append(x) | 在链表尾插入元素 xxx | O(n)(可优化为 O(1)) |
insert_after(node, x) | 在指定节点后插入元素 xxx | O(1) |
delete(node) | 删除指定节点 | O(1) |
find(x) | 查找第一个值为 xxx 的节点 | O(n) |
traverse() | 遍历整个链表 | O(n) |
is_empty() | 检查是否为空 | O(1) |
size() | 返回节点个数 | O(n) |
3、python演示
class Node:
def __init__(self, data):
self.data = data
self.next = None
class SinglyLinkedList:
def __init__(self):
self.head = None
self._size = 0 # 维护链表长度
def is_empty(self):
return self.head is None
def size(self):
return self._size
def prepend(self, x):
"""在链表头插入 —— O(1)"""
node = Node(x)
node.next = self.head
self.head = node
self._size += 1
def append(self, x):
"""在链表尾插入 —— O(n)"""
node = Node(x)
if self.is_empty():
self.head = node
else:
cur = self.head
while cur.next:
cur = cur.next
cur.next = node
self._size += 1
def insert_after(self, prev_node, x):
"""在指定节点后插入 —— O(1)"""
if not prev_node:
raise ValueError("prev_node cannot be None")
node = Node(x)
node.next = prev_node.next
prev_node.next = node
self._size += 1
def delete(self, x):
"""删除第一个值为 x 的节点 —— O(n)"""
prev, cur = None, self.head
while cur:
if cur.data == x:
if prev:
prev.next = cur.next
else:
# 删除头节点
self.head = cur.next
self._size -= 1
return True
prev, cur = cur, cur.next
return False # 未找到
def find(self, x):
"""查找第一个值为 x 的节点 —— O(n)"""
cur = self.head
while cur:
if cur.data == x:
return cur
cur = cur.next
return None
def traverse(self):
"""遍历并返回所有元素 —— O(n)"""
elems = []
cur = self.head
while cur:
elems.append(cur.data)
cur = cur.next
return elems
# === 演示 ===
if __name__ == "__main__":
ll = SinglyLinkedList()
ll.prepend(10) # [10]
ll.append(20) # [10 → 20]
ll.prepend(5) # [5 → 10 → 20]
node_10 = ll.find(10)
ll.insert_after(node_10, 15) # [5 → 10 → 15 → 20]
print("遍历:", ll.traverse()) # [5, 10, 15, 20]
ll.delete(10) # [5 → 15 → 20]
print("删除10后:", ll.traverse())
print("链表长度:", ll.size()) # 3
4、典型应用
-
LRU 缓存
-
与哈希表结合:双向链表用于维护最近访问顺序,保证 O(1)时间的插入、删除和更新。
-
-
实现其他数据结构
-
栈、队列(当底层细节需要灵活插入/删除时)
-
-
邻接表表示图
-
用链表存储每个顶点的邻居列表,节省稀疏图空间。
-
-
内存管理
-
管理空闲内存块的链表,动态分配与回收。
-
-
文本编辑器
-
用于实现 undo/redo 的操作历史链表。
-
六、哈希表
1、概念与特性
-
定义:哈希表是一种通过哈希函数(Hash Function)将键(Key)映射到数组索引的结构,用于实现平均 O(1)的插入、查找和删除。
-
核心思想:
-
对给定的键 kkk,计算哈希值 h=hash(k)
-
将 hhh 映射到数组下标 i=h mod N(N 是桶数量)
-
在桶 iii 中存放或查找键值对 (k,v)
-
2、基本操作及时间复杂度
操作 | 含义 | 平均时间复杂度 | 最坏情况 |
---|---|---|---|
put(key, value) | 插入或更新键值对 (k,v)(k,v)(k,v) | O(1) | O(n) |
get(key) | 根据键 kkk 获取对应的值 | O(1) | O(n) |
remove(key) | 删除键为 kkk 的键值对 | O(1) | O(n) |
contains(key) | 检查哈希表中是否存在键 kkk | O(1) | O(n) |
size() | 返回当前键值对总数 | O(1) | O(1) |
3、python演示
class HashTable:
def __init__(self, capacity=8):
"""初始化哈希表:桶数量 capacity,使用链表处理冲突"""
self._capacity = capacity
self._buckets = [[] for _ in range(capacity)]
self._size = 0
def _bucket_index(self, key):
"""计算键对应的桶下标"""
return hash(key) % self._capacity
def put(self, key, value):
"""插入或更新键值对"""
idx = self._bucket_index(key)
bucket = self._buckets[idx]
for i, (k, v) in enumerate(bucket):
if k == key:
bucket[i] = (key, value) # 更新
return
bucket.append((key, value)) # 新插入
self._size += 1
def get(self, key):
"""获取键对应的值,如不存在则抛出 KeyError"""
idx = self._bucket_index(key)
for k, v in self._buckets[idx]:
if k == key:
return v
raise KeyError(f"{key} not found")
def remove(self, key):
"""删除键值对,如不存在则抛出 KeyError"""
idx = self._bucket_index(key)
bucket = self._buckets[idx]
for i, (k, _) in enumerate(bucket):
if k == key:
del bucket[i]
self._size -= 1
return
raise KeyError(f"{key} not found")
def contains(self, key):
"""检查键是否存在"""
idx = self._bucket_index(key)
return any(k == key for k, _ in self._buckets[idx])
def size(self):
"""返回元素个数"""
return self._size
if __name__ == "__main__":
ht = HashTable()
ht.put("apple", 5)
ht.put("banana", 3)
ht.put("orange", 8)
print("大小:", ht.size()) # 3
print("apple 的值:", ht.get("apple")) # 5
ht.put("apple", 10) # 更新
print("更新后 apple:", ht.get("apple")) # 10
print("包含 grape? ", ht.contains("grape")) # False
ht.remove("banana")
print("删除 banana 后大小:", ht.size()) # 2
4、典型应用
-
关联数组/字典
-
编程语言中最基础的数据结构,如 Python 的
dict
,Java 的HashMap
-
-
缓存实现
-
LRU 缓存中快速定位节点
-
-
计数与去重
-
统计词频、判断字符串中字符是否唯一
-
-
符号表
-
编译原理中存储标识符信息
-
-
数据库索引
-
哈希索引用于等值查询
-
七、树
1、概念与术语
-
定义
树是一种分层的非线性数据结构,由节点(Node)构成。每个节点可以有零个或多个子节点(Child),但只有一个父节点(Parent),除根节点(Root)外。 -
术语
-
根节点(Root):没有父节点的节点
-
叶节点(Leaf):没有子节点的节点
-
内部节点(Internal Node):至少有一个子节点的节点
-
深度(Depth):从根到该节点的边数
-
高度(Height):该节点到最远叶子的边数;整个树的高度即根的高度
-
度(Degree):节点的子节点数;树的度是节点度的最大值
-
2、常见类型
类型 | 描述 |
---|---|
一般树(General Tree) | 每个节点可有任意个子节点 |
二叉树(Binary Tree) | 每个节点最多有两个子节点,分别称为左子节点(Left)和右子节点(Right) |
二叉搜索树(BST) | 左子树所有节点 < 根节点 < 右子树所有节点,支持 O(logn)的查找、插入、删除 |
平衡二叉树 (AVL/红黑树) | 通过旋转等操作保持高度平衡,确保最坏 O(logn)操作 |
B 树/B+ 树 | 多路平衡搜索树,广泛用于数据库和文件系统索引 |
堆(Heap) | 满足堆序性质的完全二叉树,用于优先队列 |
字典树(Trie) | 多叉树,用于字符索引,常见于前缀检索 |
3、核心操作及时间复杂度
操作 | 描述 | 二叉树 | 二叉搜索树(平均) |
---|---|---|---|
insert(key) | 插入新节点 | O(1) | O(logn) |
search(key) | 查找值为 key 的节点 | O(n) | O(logn) |
delete(key) | 删除值为 key 的节点 | O(n) | O(logn) |
traverse(order) | 遍历:前序/中序/后序/层序 | O(n) | O(n) |
height() | 计算树的高度 | O(n) | O(n) |
is_empty() | 检查是否为空 | O(1) | O( |
4、python演示
class Node:
def __init__(self, key):
self.key = key
self.left = None
self.right = None
class BST:
def __init__(self):
self.root = None
def is_empty(self):
return self.root is None
def insert(self, key):
"""插入新键"""
self.root = self._insert(self.root, key)
def _insert(self, node, key):
if not node:
return Node(key)
if key < node.key:
node.left = self._insert(node.left, key)
elif key > node.key:
node.right = self._insert(node.right, key)
# 相等时可按需求处理(不插入或更新)
return node
def search(self, key):
"""查找键,返回节点或 None"""
return self._search(self.root, key)
def _search(self, node, key):
if not node or node.key == key:
return node
if key < node.key:
return self._search(node.left, key)
else:
return self._search(node.right, key)
def inorder(self):
"""中序遍历(升序输出)"""
return list(self._inorder(self.root))
def _inorder(self, node):
if node:
yield from self._inorder(node.left)
yield node.key
yield from self._inorder(node.right)
def preorder(self):
"""前序遍历"""
return list(self._preorder(self.root))
def _preorder(self, node):
if node:
yield node.key
yield from self._preorder(node.left)
yield from self._preorder(node.right)
def postorder(self):
"""后序遍历"""
return list(self._postorder(self.root))
def _postorder(self, node):
if node:
yield from self._postorder(node.left)
yield from self._postorder(node.right)
yield node.key
def level_order(self):
"""层序遍历(BFS)"""
from collections import deque
if not self.root:
return []
q = deque([self.root])
order = []
while q:
node = q.popleft()
order.append(node.key)
if node.left: q.append(node.left)
if node.right: q.append(node.right)
return order
# === 演示 ===
if __name__ == "__main__":
bst = BST()
for key in [10, 5, 15, 2, 7, 12, 20]:
bst.insert(key)
print("中序遍历:", bst.inorder()) # [2,5,7,10,12,15,20]
print("前序遍历:", bst.preorder()) # [10,5,2,7,15,12,20]
print("后序遍历:", bst.postorder()) # [2,7,5,12,20,15,10]
print("层序遍历:", bst.level_order()) # [10,5,15,2,7,12,20]
print("查找 7:", bool(bst.search(7))) # True
5、典型算法与应用
-
排序与查找
-
中序遍历直接得到排序序列
-
-
数据库/文件索引
-
B 树及其变种(B+ 树)
-
-
符号表
-
编译器中的标识符存储
-
-
优先级任务管理
-
堆(完全二叉树)实现优先队列
-
-
表达式解析
-
抽象语法树(AST)表示程序结构
-
-
路径搜索
-
决策树、搜索树(如 A*)等
-
八、图
1、概念与术语
-
定义:图由一组顶点(Vertices/Nodes)和一组边(Edges)构成,用于表示任意对象间的关系。
-
表示:
-
顶点集 V={v1,v2,… }
-
边集 E={(u,v)∣u,v∈V}
-
2、分类
类型 | 描述 |
---|---|
无向图 | 边 (u,v)与 (v,u)等价,表示双向关系 |
有向图(Digraph) | 边有方向,(u,v)≠(v,u)(,表示单向关系 |
带权图 | 每条边 (u,v) 带有权重 w(u,v),常用于最短路径、网络流等 |
稠密图/稀疏图 | 根据边数与顶点数比重:稠密图 E≈V2次方,稀疏图 E≪V2次方 |
简单图/多重图 | 简单图无平行边;多重图允许平行边;自环视具体问题可有可无 |
3、存储结构与复杂度
结构 | 空间复杂度 | 查找邻居 | 插入边 | 删除边 |
---|---|---|---|---|
邻接矩阵 | (O( | V | ^2)) | (O( |
邻接表 | (O( | V | + | E |
边列表(Edge List) | (O( | E | )) | (O( |
4、核心操作与复杂度
操作 | 邻接矩阵 | 邻接表 |
---|---|---|
add_vertex() | (O( | V |
add_edge(u,v) | O(1) | O(1) |
remove_edge(u,v) | O(1) | O(deg(u)) |
has_edge(u,v) | O(1) | O(deg(u)) |
neighbors(u) | (O( | V |
is_empty() | O(1) | O(1) |
5、python演示
class Graph:
def __init__(self, directed=False):
self.directed = directed
self.adj = {} # 字典:顶点 -> 邻居列表
def add_vertex(self, v):
if v not in self.adj:
self.adj[v] = []
def add_edge(self, u, v, weight=None):
"""插入边 (u,v),若无向图则同时插入 (v,u)"""
self.add_vertex(u)
self.add_vertex(v)
self.adj[u].append((v, weight))
if not self.directed:
self.adj[v].append((u, weight))
def remove_edge(self, u, v):
self.adj[u] = [(nbr,w) for nbr,w in self.adj[u] if nbr != v]
if not self.directed:
self.adj[v] = [(nbr,w) for nbr,w in self.adj[v] if nbr != u]
def has_edge(self, u, v):
return any(nbr == v for nbr,_ in self.adj.get(u, []))
def neighbors(self, v):
return [nbr for nbr,_ in self.adj.get(v, [])]
def __str__(self):
return '\n'.join(f"{v}: {self.adj[v]}" for v in self.adj)
if __name__ == "__main__":
g = Graph(directed=False)
g.add_edge('A', 'B')
g.add_edge('A', 'C')
g.add_edge('B', 'D', weight=2)
g.add_edge('C', 'D', weight=3)
g.add_edge('D', 'E')
print("当前图的邻接表:")
print(g)
# 输出示例:
# A: [('B', None), ('C', None)]
# B: [('A', None), ('D', 2)]
# C: [('A', None), ('D', 3)]
# D: [('B', 2), ('C', 3), ('E', None)]
# E: [('D', None)]
6、典型算法与应用
-
遍历算法
-
深度优先搜索(DFS)
-
广度优先搜索(BFS)
-
-
最短路径
-
Dijkstra、Bellman-Ford、Floyd-Warshall
-
-
最小生成树
-
Kruskal、Prim
-
-
网络流
-
Edmonds-Karp、Dinic
-
-
拓扑排序(有向无环图)
-
社区发现、连接分量
-
强连通分量、弱连通分量
-