bt

数据结构与算法完整教程

· 100 min read · 数据结构 , 算法 , 考试

所有代码 C 和 C++ 双版本。C 用 malloc/free + struct,C++ 用 new/delete + class + STL。


目录

  1. 复杂度分析
  2. 树与二叉树
  3. 查找
  4. 排序

1. 复杂度分析

1.1 时间复杂度

一句话:代码执行次数和数据量 n 的关系。

// O(n) — 循环一次
for (int i = 0; i < n; i++) {
printf("%d\n", i);
}
// O(n²) — 嵌套循环
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
printf("%d %d\n", i, j);
}
}
// O(log n) — 每次砍一半
int i = n;
while (i > 1) {
i = i / 2; // 比如二分查找
}
// O(1) — 管你 n 多大,我一步完事
int x = arr[5]; // 数组下标访问

常见排序:

复杂度名称例子
O(1)常数数组随机访问
O(log n)对数二分查找、平衡树
O(n)线性遍历数组
O(n log n)线性对数快排、归并排序
O(n²)平方冒泡排序、选择排序
O(2ⁿ)指数递归求斐波那契(无缓存)

1.2 空间复杂度

你的算法额外用了多少内存。

// O(1) 空间 — 只用了几个变量
int sum(int arr[], int n) {
int total = 0;
for (int i = 0; i < n; i++) {
total += arr[i];
}
return total;
}
// O(n) 空间 — 新建了一个等长数组
int* double_arr(int arr[], int n) {
int* result = (int*)malloc(n * sizeof(int));
for (int i = 0; i < n; i++) {
result[i] = arr[i] * 2;
}
return result; // 调用者负责 free
}

1.3 递归的时间复杂度

Master Theorem(主定理)速查:

T(n) = a × T(n/b) + f(n)
情况1:f(n) = O(n^(log_b(a) - ε)) → T(n) = Θ(n^(log_b(a)))
情况2:f(n) = Θ(n^(log_b(a))) → T(n) = Θ(n^(log_b(a)) × log n)
情况3:f(n) = Ω(n^(log_b(a) + ε)) → T(n) = Θ(f(n))

实战速查(直接背):

算法递推式复杂度
二分查找T(n) = T(n/2) + O(1)O(log n)
归并排序T(n) = 2T(n/2) + O(n)O(n log n)
遍历二叉树T(n) = 2T(n/2) + O(1)O(n)
快排(平均)T(n) = 2T(n/2) + O(n)O(n log n)

2. 树与二叉树

2.1 树的基本概念

A ← 根节点 (root)
/ \
B C ← 内部节点 (internal node)
/ \ \
D E F ← 叶节点 (leaf node)

核心术语:

  • 高度 (Height):从该节点到最远叶子的边数。叶子高度=0。
  • 深度 (Depth):从根到该节点的边数。根深度=0。
  • 度 (Degree):一个节点有几个孩子。
  • 子树 (Subtree):以某个节点为根的树。

孩子兄弟表示法(最通用)

// ===== C 版 =====
typedef struct TreeNode {
int val;
struct TreeNode* first_child; // 第一个孩子
struct TreeNode* next_sibling; // 下一个兄弟
} TreeNode;
// ===== C++ 版 =====
struct TreeNode {
int val;
TreeNode* first_child = nullptr;
TreeNode* next_sibling = nullptr;
};

大部分场景直接用孩子列表:

// C++ 推荐
#include <vector>
struct TreeNode {
int val;
std::vector<TreeNode*> children;
};

2.2 二叉树

每个节点最多两个孩子的树。

// ===== C 版 =====
typedef struct BinTreeNode {
int val;
struct BinTreeNode* left;
struct BinTreeNode* right;
} BinTreeNode;
// 创建节点
BinTreeNode* create_node(int val) {
BinTreeNode* node = (BinTreeNode*)malloc(sizeof(BinTreeNode));
node->val = val;
node->left = NULL;
node->right = NULL;
return node;
}
// ===== C++ 版 =====
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

2.2.1 三种遍历 — 核心中的核心

1
/ \
2 3
/ \ \
4 5 6
前序 (根左右): 1 2 4 5 3 6
中序 (左根右): 4 2 5 1 3 6
后序 (左右根): 4 5 2 6 3 1
层序 (bfs): 1 2 3 4 5 6
// ===== C 版:递归 =====
void preorder(BinTreeNode* root) {
if (!root) return;
printf("%d ", root->val);
preorder(root->left);
preorder(root->right);
}
void inorder(BinTreeNode* root) {
if (!root) return;
inorder(root->left);
printf("%d ", root->val);
inorder(root->right);
}
void postorder(BinTreeNode* root) {
if (!root) return;
postorder(root->left);
postorder(root->right);
printf("%d ", root->val);
}
// ===== C++ 版:递归 =====
void preorder(TreeNode* root) {
if (!root) return;
cout << root->val << " ";
preorder(root->left);
preorder(root->right);
}
// ===== C 版:迭代前序 =====
// 栈:先压右再压左,弹出时先左后右
void preorder_iter(BinTreeNode* root) {
if (!root) return;
BinTreeNode** stack = (BinTreeNode**)malloc(100 * sizeof(BinTreeNode*));
int top = -1;
stack[++top] = root;
while (top >= 0) {
BinTreeNode* node = stack[top--];
printf("%d ", node->val);
if (node->right) stack[++top] = node->right;
if (node->left) stack[++top] = node->left;
}
free(stack);
}
// ===== C 版:迭代中序(一路往左走)=====
void inorder_iter(BinTreeNode* root) {
BinTreeNode** stack = (BinTreeNode**)malloc(100 * sizeof(BinTreeNode*));
int top = -1;
BinTreeNode* cur = root;
while (cur || top >= 0) {
while (cur) { // 一路走到最左
stack[++top] = cur;
cur = cur->left;
}
cur = stack[top--]; // 弹出最左
printf("%d ", cur->val);
cur = cur->right; // 转向右子树
}
free(stack);
}
// ===== C 版:迭代后序(前序(根右左) 反转)=====
void postorder_iter(BinTreeNode* root) {
if (!root) return;
int* result = (int*)malloc(100 * sizeof(int)); // 存结果
int idx = 0;
BinTreeNode** stack = (BinTreeNode**)malloc(100 * sizeof(BinTreeNode*));
int top = -1;
stack[++top] = root;
while (top >= 0) {
BinTreeNode* node = stack[top--];
result[idx++] = node->val; // 存放(根右左顺序)
if (node->left) stack[++top] = node->left;
if (node->right) stack[++top] = node->right;
}
// 反转输出
for (int i = idx - 1; i >= 0; i--) {
printf("%d ", result[i]);
}
free(result);
free(stack);
}
// ===== C++ 版:迭代(用 STL stack)=====
#include <stack>
#include <vector>
void preorder_iter(TreeNode* root) {
if (!root) return;
std::stack<TreeNode*> stk;
stk.push(root);
while (!stk.empty()) {
TreeNode* node = stk.top(); stk.pop();
cout << node->val << " ";
if (node->right) stk.push(node->right);
if (node->left) stk.push(node->left);
}
}
void inorder_iter(TreeNode* root) {
std::stack<TreeNode*> stk;
TreeNode* cur = root;
while (cur || !stk.empty()) {
while (cur) {
stk.push(cur);
cur = cur->left;
}
cur = stk.top(); stk.pop();
cout << cur->val << " ";
cur = cur->right;
}
}
void postorder_iter(TreeNode* root) {
if (!root) return;
std::vector<int> result;
std::stack<TreeNode*> stk;
stk.push(root);
while (!stk.empty()) {
TreeNode* node = stk.top(); stk.pop();
result.push_back(node->val);
if (node->left) stk.push(node->left);
if (node->right) stk.push(node->right);
}
for (auto it = result.rbegin(); it != result.rend(); ++it) {
cout << *it << " ";
}
}
// ===== 层序遍历 (BFS) =====
// C 版:用数组模拟队列
#include <stdio.h>
#include <stdlib.h>
void level_order(BinTreeNode* root) {
if (!root) return;
BinTreeNode** queue = (BinTreeNode**)malloc(100 * sizeof(BinTreeNode*));
int front = 0, rear = 0;
queue[rear++] = root;
while (front < rear) {
BinTreeNode* node = queue[front++];
printf("%d ", node->val);
if (node->left) queue[rear++] = node->left;
if (node->right) queue[rear++] = node->right;
}
free(queue);
}
// C++ 版:用 std::queue
#include <queue>
void level_order(TreeNode* root) {
if (!root) return;
std::queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
TreeNode* node = q.front(); q.pop();
cout << node->val << " ";
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
}

2.3 二叉搜索树 (BST)

规则:左子树的值 < 根的值 < 右子树的值。

8
/ \
3 10
/ \ \
1 6 14
/ \ /
4 7 13
// ===== C 版 =====
#include <stdio.h>
#include <stdlib.h>
typedef struct BSTNode {
int val;
struct BSTNode* left;
struct BSTNode* right;
} BSTNode;
BSTNode* bst_create(int val) {
BSTNode* node = (BSTNode*)malloc(sizeof(BSTNode));
node->val = val;
node->left = node->right = NULL;
return node;
}
// 查找:一路比大小
BSTNode* bst_search(BSTNode* root, int val) {
BSTNode* cur = root;
while (cur) {
if (val == cur->val)
return cur;
else if (val < cur->val)
cur = cur->left;
else
cur = cur->right;
}
return NULL;
}
// 插入
BSTNode* bst_insert(BSTNode* root, int val) {
if (!root) return bst_create(val);
BSTNode* cur = root;
while (1) {
if (val < cur->val) {
if (cur->left)
cur = cur->left;
else {
cur->left = bst_create(val);
break;
}
} else {
if (cur->right)
cur = cur->right;
else {
cur->right = bst_create(val);
break;
}
}
}
return root;
}
// 找最小值
BSTNode* bst_min(BSTNode* node) {
while (node->left) node = node->left;
return node;
}
// 删除
BSTNode* bst_delete(BSTNode* root, int val) {
if (!root) return NULL;
if (val < root->val)
root->left = bst_delete(root->left, val);
else if (val > root->val)
root->right = bst_delete(root->right, val);
else {
// 情况1+2:0个或1个孩子
if (!root->left) {
BSTNode* tmp = root->right;
free(root);
return tmp;
}
if (!root->right) {
BSTNode* tmp = root->left;
free(root);
return tmp;
}
// 情况3:两个小孩 → 找后继(右子树最小),替换
BSTNode* successor = bst_min(root->right);
root->val = successor->val;
root->right = bst_delete(root->right, successor->val);
}
return root;
}
// ===== C++ 版 =====
class BST {
public:
struct Node {
int val;
Node* left;
Node* right;
Node(int x) : val(x), left(nullptr), right(nullptr) {}
};
Node* root = nullptr;
Node* search(int val) {
Node* cur = root;
while (cur) {
if (val == cur->val) return cur;
cur = (val < cur->val) ? cur->left : cur->right;
}
return nullptr;
}
void insert(int val) {
if (!root) { root = new Node(val); return; }
Node* cur = root;
while (true) {
if (val < cur->val) {
if (cur->left) cur = cur->left;
else { cur->left = new Node(val); break; }
} else {
if (cur->right) cur = cur->right;
else { cur->right = new Node(val); break; }
}
}
}
Node* _min(Node* node) {
while (node->left) node = node->left;
return node;
}
Node* _delete(Node* node, int val) {
if (!node) return nullptr;
if (val < node->val)
node->left = _delete(node->left, val);
else if (val > node->val)
node->right = _delete(node->right, val);
else {
if (!node->left) { Node* tmp = node->right; delete node; return tmp; }
if (!node->right) { Node* tmp = node->left; delete node; return tmp; }
Node* succ = _min(node->right);
node->val = succ->val;
node->right = _delete(node->right, succ->val);
}
return node;
}
void remove(int val) { root = _delete(root, val); }
};

2.4 平衡二叉树 — AVL 树

// ===== C 版 =====
typedef struct AVLNode {
int val;
struct AVLNode* left;
struct AVLNode* right;
int height;
} AVLNode;
AVLNode* avl_create(int val) {
AVLNode* node = (AVLNode*)malloc(sizeof(AVLNode));
node->val = val;
node->left = node->right = NULL;
node->height = 1;
return node;
}
int height(AVLNode* n) { return n ? n->height : 0; }
int max(int a, int b) { return a > b ? a : b; }
int balance_factor(AVLNode* n) { return n ? height(n->left) - height(n->right) : 0; }
void update_height(AVLNode* n) {
n->height = 1 + max(height(n->left), height(n->right));
}
// 右旋
AVLNode* rotate_right(AVLNode* y) {
AVLNode* x = y->left;
AVLNode* T2 = x->right;
x->right = y;
y->left = T2;
update_height(y);
update_height(x);
return x;
}
// 左旋
AVLNode* rotate_left(AVLNode* x) {
AVLNode* y = x->right;
AVLNode* T2 = y->left;
y->left = x;
x->right = T2;
update_height(x);
update_height(y);
return y;
}
AVLNode* avl_insert(AVLNode* node, int val) {
// 1. 普通 BST 插入
if (!node) return avl_create(val);
if (val < node->val)
node->left = avl_insert(node->left, val);
else if (val > node->val)
node->right = avl_insert(node->right, val);
else
return node; // 重复值不管
// 2. 更新高度
update_height(node);
// 3. 检查平衡 + 旋转
int balance = balance_factor(node);
// LL
if (balance > 1 && val < node->left->val)
return rotate_right(node);
// RR
if (balance < -1 && val > node->right->val)
return rotate_left(node);
// LR
if (balance > 1 && val > node->left->val) {
node->left = rotate_left(node->left);
return rotate_right(node);
}
// RL
if (balance < -1 && val < node->right->val) {
node->right = rotate_right(node->right);
return rotate_left(node);
}
return node;
}
// ===== C++ 版 =====
struct AVLNode {
int val;
AVLNode* left;
AVLNode* right;
int height;
AVLNode(int x) : val(x), left(nullptr), right(nullptr), height(1) {}
};
int get_height(AVLNode* n) { return n ? n->height : 0; }
int get_balance(AVLNode* n) { return n ? get_height(n->left) - get_height(n->right) : 0; }
AVLNode* rotate_right(AVLNode* y) {
AVLNode* x = y->left;
AVLNode* T2 = x->right;
x->right = y;
y->left = T2;
y->height = 1 + std::max(get_height(y->left), get_height(y->right));
x->height = 1 + std::max(get_height(x->left), get_height(x->right));
return x;
}
AVLNode* rotate_left(AVLNode* x) {
AVLNode* y = x->right;
AVLNode* T2 = y->left;
y->left = x;
x->right = T2;
x->height = 1 + std::max(get_height(x->left), get_height(x->right));
y->height = 1 + std::max(get_height(y->left), get_height(y->right));
return y;
}
AVLNode* avl_insert(AVLNode* node, int val) {
if (!node) return new AVLNode(val);
if (val < node->val)
node->left = avl_insert(node->left, val);
else if (val > node->val)
node->right = avl_insert(node->right, val);
else
return node;
node->height = 1 + std::max(get_height(node->left), get_height(node->right));
int balance = get_balance(node);
if (balance > 1 && val < node->left->val) // LL
return rotate_right(node);
if (balance < -1 && val > node->right->val) // RR
return rotate_left(node);
if (balance > 1 && val > node->left->val) { // LR
node->left = rotate_left(node->left);
return rotate_right(node);
}
if (balance < -1 && val < node->right->val) { // RL
node->right = rotate_right(node->right);
return rotate_left(node);
}
return node;
}

四种旋转快速记忆:

LL: y x
/ \ / \
x T4 → T1 y
/ \ / \
T1 T2 T2 T4
LR: z z x
/ \ / \ / \
y T4 → x T4 → y z
/ \ / \ / \ / \
T1 x y T3 T1 T2 T3 T4
/ \ / \
T2 T3 T1 T2
RR: x y
/ \ / \
T1 y → x T4
/ \ / \
T2 T4 T1 T2
RL: z z x
/ \ / \ / \
T1 y → T1 x → z y
/ \ / \ / \ / \
x T4 T2 y T1 T2 T3 T4
/ \ / \
T2 T3 T3 T4

2.5 红黑树 — 工业界的宠儿

五条铁律:

  1. 节点要么红,要么黑
  2. 根节点是黑的
  3. 叶子(NIL)是黑的
  4. 红节点的两个孩子必须是黑的(不能连续两个红)
  5. 任意节点到其所有后代叶子的路径上,黑色节点数量相同
// ===== C 版 =====
#include <stdio.h>
#include <stdlib.h>
#define RED 1
#define BLACK 0
typedef struct RBNode {
int val;
int color;
struct RBNode* left;
struct RBNode* right;
struct RBNode* parent;
} RBNode;
RBNode* NIL; // 哨兵节点(全局,黑色叶节点)
RBNode* rb_create(int val, int color) {
RBNode* node = (RBNode*)malloc(sizeof(RBNode));
node->val = val;
node->color = color;
node->left = NIL;
node->right = NIL;
node->parent = NULL;
return node;
}
void rb_init() {
NIL = (RBNode*)malloc(sizeof(RBNode));
NIL->color = BLACK;
NIL->left = NIL->right = NIL->parent = NULL;
}
void rb_left_rotate(RBNode** root, RBNode* x) {
RBNode* y = x->right;
x->right = y->left;
if (y->left != NIL) y->left->parent = x;
y->parent = x->parent;
if (!x->parent) *root = y;
else if (x == x->parent->left) x->parent->left = y;
else x->parent->right = y;
y->left = x;
x->parent = y;
}
void rb_right_rotate(RBNode** root, RBNode* y) {
RBNode* x = y->left;
y->left = x->right;
if (x->right != NIL) x->right->parent = y;
x->parent = y->parent;
if (!y->parent) *root = x;
else if (y == y->parent->right) y->parent->right = x;
else y->parent->left = x;
x->right = y;
y->parent = x;
}
void rb_insert_fixup(RBNode** root, RBNode* z) {
while (z->parent && z->parent->color == RED) {
if (z->parent == z->parent->parent->left) {
RBNode* y = z->parent->parent->right; // 叔叔
if (y->color == RED) { // 情况1:叔叔红
z->parent->color = BLACK;
y->color = BLACK;
z->parent->parent->color = RED;
z = z->parent->parent;
} else {
if (z == z->parent->right) { // 情况2
z = z->parent;
rb_left_rotate(root, z);
}
z->parent->color = BLACK; // 情况3
z->parent->parent->color = RED;
rb_right_rotate(root, z->parent->parent);
}
} else { // 对称
RBNode* y = z->parent->parent->left;
if (y->color == RED) {
z->parent->color = BLACK;
y->color = BLACK;
z->parent->parent->color = RED;
z = z->parent->parent;
} else {
if (z == z->parent->left) {
z = z->parent;
rb_right_rotate(root, z);
}
z->parent->color = BLACK;
z->parent->parent->color = RED;
rb_left_rotate(root, z->parent->parent);
}
}
}
(*root)->color = BLACK;
}
void rb_insert(RBNode** root, int val) {
RBNode* z = rb_create(val, RED);
RBNode* y = NULL;
RBNode* x = *root;
while (x != NIL) {
y = x;
x = (val < x->val) ? x->left : x->right;
}
z->parent = y;
if (!y) *root = z;
else if (val < y->val) y->left = z;
else y->right = z;
rb_insert_fixup(root, z);
}
// ===== C++ 版 =====
#include <iostream>
enum Color { RED, BLACK };
template<typename T>
struct RBNode {
T val;
Color color;
RBNode *left, *right, *parent;
RBNode(T x) : val(x), color(RED), left(nullptr), right(nullptr), parent(nullptr) {}
};
template<typename T>
class RedBlackTree {
RBNode<T>* root;
RBNode<T>* NIL; // 哨兵
void left_rotate(RBNode<T>* x) {
RBNode<T>* y = x->right;
x->right = y->left;
if (y->left != NIL) y->left->parent = x;
y->parent = x->parent;
if (!x->parent) root = y;
else if (x == x->parent->left) x->parent->left = y;
else x->parent->right = y;
y->left = x;
x->parent = y;
}
void right_rotate(RBNode<T>* y) {
RBNode<T>* x = y->left;
y->left = x->right;
if (x->right != NIL) x->right->parent = y;
x->parent = y->parent;
if (!y->parent) root = x;
else if (y == y->parent->right) y->parent->right = x;
else y->parent->left = x;
x->right = y;
y->parent = x;
}
void insert_fixup(RBNode<T>* z) {
while (z->parent && z->parent->color == RED) {
if (z->parent == z->parent->parent->left) {
RBNode<T>* y = z->parent->parent->right;
if (y->color == RED) {
z->parent->color = BLACK;
y->color = BLACK;
z->parent->parent->color = RED;
z = z->parent->parent;
} else {
if (z == z->parent->right) {
z = z->parent;
left_rotate(z);
}
z->parent->color = BLACK;
z->parent->parent->color = RED;
right_rotate(z->parent->parent);
}
} else {
RBNode<T>* y = z->parent->parent->left;
if (y->color == RED) {
z->parent->color = BLACK;
y->color = BLACK;
z->parent->parent->color = RED;
z = z->parent->parent;
} else {
if (z == z->parent->left) {
z = z->parent;
right_rotate(z);
}
z->parent->color = BLACK;
z->parent->parent->color = RED;
left_rotate(z->parent->parent);
}
}
}
root->color = BLACK;
}
public:
RedBlackTree() {
NIL = new RBNode<T>(0);
NIL->color = BLACK;
NIL->left = NIL->right = NIL->parent = nullptr;
root = NIL;
}
void insert(T val) {
RBNode<T>* z = new RBNode<T>(val);
z->left = z->right = NIL;
RBNode<T>* y = nullptr;
RBNode<T>* x = root;
while (x != NIL) {
y = x;
x = (val < x->val) ? x->left : x->right;
}
z->parent = y;
if (!y) root = z;
else if (val < y->val) y->left = z;
else y->right = z;
insert_fixup(z);
}
};

为什么工业界用红黑树而不是 AVL?

  • AVL 严格平衡 → 查找快,但插入删除旋转多
  • 红黑树放松平衡 → 查找略慢(最多多一层),但插入删除旋转少(最多 3 次)
  • C++ std::map、Java TreeMap、Linux CFS 调度器、epoll 全部用红黑树

2.6 堆 (Heap)

堆是一棵完全二叉树,用数组存储。

最小堆规则:父 ≤ 子(根最小)
数组:父 (i-1)/2,左子 2i+1,右子 2i+2
1
/ \
3 5
/ \ /
7 9 6
// ===== C 版:最小堆 =====
typedef struct {
int* data;
int size;
int capacity;
} MinHeap;
MinHeap* heap_create(int cap) {
MinHeap* h = (MinHeap*)malloc(sizeof(MinHeap));
h->data = (int*)malloc(cap * sizeof(int));
h->size = 0;
h->capacity = cap;
return h;
}
void heap_sift_up(MinHeap* h, int i) {
while (i > 0) {
int parent = (i - 1) / 2;
if (h->data[i] < h->data[parent]) {
int tmp = h->data[i];
h->data[i] = h->data[parent];
h->data[parent] = tmp;
i = parent;
} else break;
}
}
void heap_sift_down(MinHeap* h, int i) {
while (1) {
int smallest = i;
int left = 2 * i + 1;
int right = 2 * i + 2;
if (left < h->size && h->data[left] < h->data[smallest])
smallest = left;
if (right < h->size && h->data[right] < h->data[smallest])
smallest = right;
if (smallest == i) break;
int tmp = h->data[i];
h->data[i] = h->data[smallest];
h->data[smallest] = tmp;
i = smallest;
}
}
void heap_push(MinHeap* h, int val) {
if (h->size >= h->capacity) return; // 需要扩容,简化处理
h->data[h->size] = val;
heap_sift_up(h, h->size);
h->size++;
}
int heap_pop(MinHeap* h) {
if (h->size == 0) return -1;
int val = h->data[0];
h->data[0] = h->data[--h->size];
if (h->size > 0) heap_sift_down(h, 0);
return val;
}
int heap_top(MinHeap* h) { return h->size > 0 ? h->data[0] : -1; }
// O(n) 建堆:从最后一个非叶节点向前 sift_down
void heap_build(int arr[], int n) {
for (int i = n / 2 - 1; i >= 0; i--) {
// sift_down on arr[i]
int cur = i;
while (1) {
int smallest = cur;
int left = 2 * cur + 1;
int right = 2 * cur + 2;
if (left < n && arr[left] < arr[smallest]) smallest = left;
if (right < n && arr[right] < arr[smallest]) smallest = right;
if (smallest == cur) break;
int tmp = arr[cur];
arr[cur] = arr[smallest];
arr[smallest] = tmp;
cur = smallest;
}
}
}
// ===== C++ 版 =====
#include <queue>
#include <vector>
// STL 直接调:默认大顶堆
std::priority_queue<int> max_heap;
std::priority_queue<int, std::vector<int>, std::greater<int>> min_heap;
max_heap.push(5);
int top = max_heap.top();
max_heap.pop();
// 手写
class MinHeap {
std::vector<int> data;
public:
void push(int val) {
data.push_back(val);
int i = data.size() - 1;
while (i > 0) {
int p = (i - 1) / 2;
if (data[i] < data[p]) {
std::swap(data[i], data[p]);
i = p;
} else break;
}
}
int pop() {
if (data.empty()) return -1;
int val = data[0];
data[0] = data.back();
data.pop_back();
int i = 0, n = data.size();
while (true) {
int smallest = i;
int l = 2 * i + 1, r = 2 * i + 2;
if (l < n && data[l] < data[smallest]) smallest = l;
if (r < n && data[r] < data[smallest]) smallest = r;
if (smallest == i) break;
std::swap(data[i], data[smallest]);
i = smallest;
}
return val;
}
int top() { return data.empty() ? -1 : data[0]; }
bool empty() { return data.empty(); }
};

2.7 哈夫曼树

// C 版:哈夫曼编码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct HuffNode {
char ch;
int freq;
struct HuffNode* left;
struct HuffNode* right;
} HuffNode;
// 用最小堆来构造(简单起见,直接选最小的两个)
HuffNode* build_huffman(char chars[], int freqs[], int n) {
// 把每个字符做成节点,放到"森林"里
HuffNode** nodes = (HuffNode**)malloc(n * sizeof(HuffNode*));
for (int i = 0; i < n; i++) {
nodes[i] = (HuffNode*)malloc(sizeof(HuffNode));
nodes[i]->ch = chars[i];
nodes[i]->freq = freqs[i];
nodes[i]->left = nodes[i]->right = NULL;
}
int size = n;
while (size > 1) {
// 找最小和次小的两个
int min1 = 0, min2 = 1;
if (nodes[min1]->freq > nodes[min2]->freq) {
int tmp = min1; min1 = min2; min2 = tmp;
}
for (int i = 2; i < size; i++) {
if (nodes[i]->freq < nodes[min1]->freq) {
min2 = min1;
min1 = i;
} else if (nodes[i]->freq < nodes[min2]->freq) {
min2 = i;
}
}
// 合并
HuffNode* parent = (HuffNode*)malloc(sizeof(HuffNode));
parent->ch = '\0';
parent->freq = nodes[min1]->freq + nodes[min2]->freq;
parent->left = nodes[min1];
parent->right = nodes[min2];
// 替换掉 min1,删除 min2
nodes[min1] = parent;
nodes[min2] = nodes[size - 1];
size--;
}
HuffNode* root = nodes[0];
free(nodes);
return root;
}
void print_codes(HuffNode* root, char* code, int depth) {
if (!root->left && !root->right) {
code[depth] = '\0';
printf(" %c : %s\n", root->ch, code);
return;
}
code[depth] = '0';
print_codes(root->left, code, depth + 1);
code[depth] = '1';
print_codes(root->right, code, depth + 1);
}
// 使用示例:
// char chars[] = {'a','b','c','d','e','f'};
// int freqs[] = {45, 13, 12, 16, 9, 5};
// HuffNode* tree = build_huffman(chars, freqs, 6);
// char code[100];
// print_codes(tree, code, 0);

3. 图

3.1 图的基本概念

0 --- 1
| \ |
| \ |
3 --- 2

三种存储方式:

// ===== 1. 邻接矩阵 — O(V²),稠密图 =====
#define MAX_V 100
int graph[MAX_V][MAX_V]; // 0=无边, 1=有边 (或存权重)
// 访问: graph[0][1] = 1 表示 0→1 有边
// ===== 2. 邻接表 — O(V+E),最常用 =====
// C 版
#define MAX_V 100
typedef struct EdgeNode {
int dest;
int weight;
struct EdgeNode* next;
} EdgeNode;
typedef struct {
EdgeNode* head;
} AdjList[MAX_V];
void add_edge(AdjList graph, int src, int dest) {
EdgeNode* node = (EdgeNode*)malloc(sizeof(EdgeNode));
node->dest = dest;
node->weight = 1;
node->next = graph[src].head;
graph[src].head = node;
}
// C++ 版
#include <vector>
#include <utility>
std::vector<std::pair<int, int>> adj[100]; // adj[u] = {(v, w), ...}
void add_edge(int u, int v, int w = 1) {
adj[u].push_back({v, w});
}
// ===== 3. 边列表 — O(E),Kruskal 用 =====
struct Edge {
int u, v, w;
};
std::vector<Edge> edges = {{0,1,4}, {0,2,1}, {1,3,1}, {2,1,2}};

3.2 DFS 深度优先搜索

// ===== C 版 =====
#include <stdbool.h>
bool visited[MAX_V];
void dfs_c(AdjList graph, int v) {
visited[v] = true;
printf("%d ", v);
for (EdgeNode* e = graph[v].head; e; e = e->next) {
if (!visited[e->dest]) {
dfs_c(graph, e->dest);
}
}
}
void dfs_all(AdjList graph, int V) {
memset(visited, 0, sizeof(visited));
for (int i = 0; i < V; i++) {
if (!visited[i]) dfs_c(graph, i);
}
}
// ===== C++ 版 =====
#include <vector>
using namespace std;
void dfs(vector<int> adj[], int v, bool visited[]) {
visited[v] = true;
cout << v << " ";
for (int neighbor : adj[v]) {
if (!visited[neighbor]) dfs(adj, neighbor, visited);
}
}
// 迭代版(栈)
void dfs_iter(vector<int> adj[], int start, int V) {
bool visited[V] = {false};
stack<int> stk;
stk.push(start);
while (!stk.empty()) {
int v = stk.top(); stk.pop();
if (!visited[v]) {
visited[v] = true;
cout << v << " ";
// 倒序压栈,顺序不变
for (auto it = adj[v].rbegin(); it != adj[v].rend(); ++it) {
if (!visited[*it]) stk.push(*it);
}
}
}
}

DFS 四大经典应用:

// ===== 1. 检测环(无向图)=====
bool has_cycle_util(vector<int> adj[], int v, bool visited[], int parent) {
visited[v] = true;
for (int neighbor : adj[v]) {
if (!visited[neighbor]) {
if (has_cycle_util(adj, neighbor, visited, v))
return true;
} else if (neighbor != parent) {
return true; // 访问过且不是父节点 = 有环
}
}
return false;
}
bool has_cycle(vector<int> adj[], int V) {
bool visited[V] = {false};
for (int i = 0; i < V; i++) {
if (!visited[i]) {
if (has_cycle_util(adj, i, visited, -1))
return true;
}
}
return false;
}
// ===== 2. 拓扑排序(有向无环图 DAG)=====
void topo_dfs(vector<int> adj[], int v, bool visited[], stack<int>& stk) {
visited[v] = true;
for (int neighbor : adj[v]) {
if (!visited[neighbor]) topo_dfs(adj, neighbor, visited, stk);
}
stk.push(v); // 后序
}
vector<int> topological_sort(vector<int> adj[], int V) {
bool visited[V] = {false};
stack<int> stk;
for (int i = 0; i < V; i++) {
if (!visited[i]) topo_dfs(adj, i, visited, stk);
}
vector<int> result;
while (!stk.empty()) {
result.push_back(stk.top()); stk.pop();
}
return result; // 后序反转 = 拓扑序
}
// ===== 3. 连通分量 =====
void dfs_cc(vector<int> adj[], int v, bool visited[], vector<int>& comp) {
visited[v] = true;
comp.push_back(v);
for (int neighbor : adj[v]) {
if (!visited[neighbor]) dfs_cc(adj, neighbor, visited, comp);
}
}
vector<vector<int>> connected_components(vector<int> adj[], int V) {
bool visited[V] = {false};
vector<vector<int>> components;
for (int i = 0; i < V; i++) {
if (!visited[i]) {
vector<int> comp;
dfs_cc(adj, i, visited, comp);
components.push_back(comp);
}
}
return components;
}
// ===== 4. 二分图检测 =====
bool is_bipartite_util(vector<int> adj[], int v, int color[]) {
for (int neighbor : adj[v]) {
if (color[neighbor] == -1) {
color[neighbor] = 1 - color[v];
if (!is_bipartite_util(adj, neighbor, color))
return false;
} else if (color[neighbor] == color[v]) {
return false;
}
}
return true;
}
bool is_bipartite(vector<int> adj[], int V) {
int color[V];
fill(color, color + V, -1);
for (int i = 0; i < V; i++) {
if (color[i] == -1) {
color[i] = 0;
if (!is_bipartite_util(adj, i, color))
return false;
}
}
return true;
}

3.3 BFS 广度优先搜索

// ===== C++ 版 =====
#include <queue>
#include <vector>
using namespace std;
void bfs(vector<int> adj[], int start, int V) {
bool visited[V] = {false};
queue<int> q;
visited[start] = true;
q.push(start);
while (!q.empty()) {
int v = q.front(); q.pop();
cout << v << " ";
for (int neighbor : adj[v]) {
if (!visited[neighbor]) {
visited[neighbor] = true;
q.push(neighbor);
}
}
}
}
// 最短路径(无权图)
vector<int> shortest_path(vector<int> adj[], int V, int start, int end) {
bool visited[V] = {false};
int parent[V];
fill(parent, parent + V, -1);
queue<int> q;
visited[start] = true;
q.push(start);
while (!q.empty()) {
int v = q.front(); q.pop();
if (v == end) {
// 回溯路径
vector<int> path;
for (int cur = end; cur != -1; cur = parent[cur])
path.push_back(cur);
reverse(path.begin(), path.end());
return path;
}
for (int neighbor : adj[v]) {
if (!visited[neighbor]) {
visited[neighbor] = true;
parent[neighbor] = v;
q.push(neighbor);
}
}
}
return {}; // 不可达
}
// 层序遍历
vector<vector<int>> bfs_levels(vector<int> adj[], int V, int start) {
vector<vector<int>> levels;
bool visited[V] = {false};
queue<int> q;
visited[start] = true;
q.push(start);
while (!q.empty()) {
int size = q.size();
vector<int> level;
for (int i = 0; i < size; i++) {
int v = q.front(); q.pop();
level.push_back(v);
for (int neighbor : adj[v]) {
if (!visited[neighbor]) {
visited[neighbor] = true;
q.push(neighbor);
}
}
}
levels.push_back(level);
}
return levels;
}

3.4 Dijkstra — 单源最短路径(非负权)

// ===== C++:邻接表 + 优先队列 =====
#include <queue>
#include <vector>
#include <climits>
using namespace std;
typedef pair<int, int> pii; // {距离, 节点}
vector<int> dijkstra(vector<pii> adj[], int V, int start) {
vector<int> dist(V, INT_MAX);
dist[start] = 0;
priority_queue<pii, vector<pii>, greater<pii>> pq;
pq.push({0, start});
while (!pq.empty()) {
auto [d, v] = pq.top(); pq.pop();
if (d > dist[v]) continue; // 过期值跳过
for (auto [neighbor, w] : adj[v]) {
if (dist[v] + w < dist[neighbor]) {
dist[neighbor] = dist[v] + w;
pq.push({dist[neighbor], neighbor});
}
}
}
return dist;
}
// ===== C 版:邻接矩阵 + 手动实现 =====
#include <limits.h>
#include <stdbool.h>
void dijkstra_c(int graph[MAX_V][MAX_V], int V, int start, int dist[]) {
bool visited[MAX_V] = {false};
for (int i = 0; i < V; i++) dist[i] = INT_MAX;
dist[start] = 0;
for (int count = 0; count < V - 1; count++) {
// 选最小的未访问节点
int u = -1, min_dist = INT_MAX;
for (int i = 0; i < V; i++) {
if (!visited[i] && dist[i] < min_dist) {
min_dist = dist[i];
u = i;
}
}
if (u == -1) break;
visited[u] = true;
// 松弛
for (int v = 0; v < V; v++) {
if (!visited[v] && graph[u][v] && dist[u] != INT_MAX
&& dist[u] + graph[u][v] < dist[v]) {
dist[v] = dist[u] + graph[u][v];
}
}
}
}
// 时间复杂度: O((V+E) log V) — 堆优化版 | O(V²) — 朴素版

3.5 Bellman-Ford — 可处理负权边

// ===== C++ 版 =====
#include <climits>
#include <vector>
using namespace std;
struct Edge { int u, v, w; };
// 返回 {距离数组, 是否有负环}
pair<vector<int>, bool> bellman_ford(int V, vector<Edge>& edges, int start) {
vector<int> dist(V, INT_MAX);
dist[start] = 0;
// 松弛 V-1 轮
for (int i = 0; i < V - 1; i++) {
bool updated = false;
for (auto& e : edges) {
if (dist[e.u] != INT_MAX && dist[e.u] + e.w < dist[e.v]) {
dist[e.v] = dist[e.u] + e.w;
updated = true;
}
}
if (!updated) break; // 提前退出
}
// 第 V 轮:检测负环
for (auto& e : edges) {
if (dist[e.u] != INT_MAX && dist[e.u] + e.w < dist[e.v]) {
return {dist, true}; // 存在负权环
}
}
return {dist, false};
}
// ===== C 版 =====
bool bellman_ford_c(int V, Edge edges[], int E, int start, int dist[]) {
for (int i = 0; i < V; i++) dist[i] = INT_MAX;
dist[start] = 0;
for (int i = 0; i < V - 1; i++) {
int updated = 0;
for (int j = 0; j < E; j++) {
int u = edges[j].u, v = edges[j].v, w = edges[j].w;
if (dist[u] != INT_MAX && dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
updated = 1;
}
}
if (!updated) break;
}
for (int j = 0; j < E; j++) {
int u = edges[j].u, v = edges[j].v, w = edges[j].w;
if (dist[u] != INT_MAX && dist[u] + w < dist[v])
return true; // 负环
}
return false;
}
// 时间复杂度: O(V×E)

3.6 Floyd-Warshall — 所有点对最短路径

// ===== C++ 版 =====
// dp[k][i][j] = 只用前k个中间节点,i→j 的最短距离
const int INF = 1e9;
void floyd_warshall(vector<vector<int>>& dist, int V) {
for (int k = 0; k < V; k++) {
for (int i = 0; i < V; i++) {
for (int j = 0; j < V; j++) {
if (dist[i][k] != INF && dist[k][j] != INF
&& dist[i][k] + dist[k][j] < dist[i][j]) {
dist[i][j] = dist[i][k] + dist[k][j];
}
}
}
}
}
// ===== C 版 =====
void floyd_warshall_c(int dist[MAX_V][MAX_V], int V) {
for (int k = 0; k < V; k++) {
for (int i = 0; i < V; i++) {
for (int j = 0; j < V; j++) {
if (dist[i][k] != INT_MAX && dist[k][j] != INT_MAX
&& dist[i][k] + dist[k][j] < dist[i][j]) {
dist[i][j] = dist[i][k] + dist[k][j];
}
}
}
}
}
// 时间复杂度: O(V³)

3.7 最小生成树 MST

Prim — 点优先(稠密图)

// ===== C++ 版 =====
#include <queue>
#include <vector>
using namespace std;
typedef pair<int, int> pii; // {权重, 节点}
int prim(vector<pii> adj[], int V) {
vector<bool> in_mst(V, false);
priority_queue<pii, vector<pii>, greater<pii>> pq;
pq.push({0, 0}); // {权重, 节点}
int total_weight = 0;
int edges_in_mst = 0;
while (!pq.empty() && edges_in_mst < V) {
auto [w, v] = pq.top(); pq.pop();
if (in_mst[v]) continue;
in_mst[v] = true;
total_weight += w;
edges_in_mst++;
for (auto [neighbor, weight] : adj[v]) {
if (!in_mst[neighbor])
pq.push({weight, neighbor});
}
}
return total_weight;
}
// 时间复杂度: O((V+E) log V)

Kruskal — 边优先(稀疏图)

// ===== Kruskal + 并查集 =====
// 先实现并查集
class UnionFind {
vector<int> parent, rank;
public:
UnionFind(int n) : parent(n), rank(n, 0) {
for (int i = 0; i < n; i++) parent[i] = i;
}
int find(int x) {
if (parent[x] != x)
parent[x] = find(parent[x]); // 路径压缩
return parent[x];
}
bool unite(int x, int y) {
int rx = find(x), ry = find(y);
if (rx == ry) return false;
if (rank[rx] < rank[ry]) parent[rx] = ry;
else if (rank[rx] > rank[ry]) parent[ry] = rx;
else { parent[ry] = rx; rank[rx]++; }
return true;
}
};
struct Edge { int u, v, w; };
int kruskal(int V, vector<Edge>& edges) {
sort(edges.begin(), edges.end(),
[](Edge& a, Edge& b) { return a.w < b.w; });
UnionFind uf(V);
int total = 0;
int cnt = 0; // 已加入边数
for (auto& e : edges) {
if (uf.unite(e.u, e.v)) {
total += e.w;
cnt++;
if (cnt == V - 1) break;
}
}
return total;
}
// 时间复杂度: O(E log E) = O(E log V)
// ===== C 版:并查集 =====
typedef struct {
int parent[MAX_V];
int rank[MAX_V];
} UnionFind;
void uf_init(UnionFind* uf, int n) {
for (int i = 0; i < n; i++) {
uf->parent[i] = i;
uf->rank[i] = 0;
}
}
int uf_find(UnionFind* uf, int x) {
if (uf->parent[x] != x)
uf->parent[x] = uf_find(uf, uf->parent[x]);
return uf->parent[x];
}
bool uf_unite(UnionFind* uf, int x, int y) {
int rx = uf_find(uf, x), ry = uf_find(uf, y);
if (rx == ry) return false;
if (uf->rank[rx] < uf->rank[ry])
uf->parent[rx] = ry;
else if (uf->rank[rx] > uf->rank[ry])
uf->parent[ry] = rx;
else {
uf->parent[ry] = rx;
uf->rank[rx]++;
}
return true;
}

4. 查找

4.1 顺序查找

// O(n),一个个比
int sequential_search(int arr[], int n, int target) {
for (int i = 0; i < n; i++) {
if (arr[i] == target) return i;
}
return -1;
}

4.2 二分查找

// ===== 标准二分(C)=====
int binary_search(int arr[], int n, int target) {
int lo = 0, hi = n - 1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2; // 防溢出
if (arr[mid] == target)
return mid;
else if (arr[mid] < target)
lo = mid + 1;
else
hi = mid - 1;
}
return -1;
}
// ===== 变体1:左边界(第一个 ≥ target 的位置)=====
int lower_bound_c(int arr[], int n, int target) {
int lo = 0, hi = n;
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (arr[mid] < target)
lo = mid + 1;
else
hi = mid;
}
return lo; // arr[lo] 是第一个 ≥ target 的位置
}
// ===== 变体2:右边界(最后一个 ≤ target 的位置)=====
int upper_bound_c(int arr[], int n, int target) {
int lo = 0, hi = n;
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (arr[mid] <= target)
lo = mid + 1;
else
hi = mid;
}
return lo - 1; // arr[lo-1] 是最后一个 ≤ target 的位置
}
// C++ STL 直接调
#include <algorithm>
#include <vector>
using namespace std;
vector<int> arr = {1,2,2,2,3,5,8};
auto it = lower_bound(arr.begin(), arr.end(), 2); // → 指向第一个2
auto it = upper_bound(arr.begin(), arr.end(), 2); // → 指向3
bool found = binary_search(arr.begin(), arr.end(), 5); // → true

二分不止在有序数组用:

// 找旋转排序数组的最小值
int find_min_rotated(int arr[], int n) {
int lo = 0, hi = n - 1;
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (arr[mid] > arr[hi])
lo = mid + 1; // 最小值在右半
else
hi = mid; // 最小值在左半(含mid)
}
return arr[lo];
}
// 找峰值
int find_peak(int arr[], int n) {
int lo = 0, hi = n - 1;
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (arr[mid] < arr[mid + 1])
lo = mid + 1; // 上坡,峰值在右
else
hi = mid; // 下坡,峰值在左(含mid)
}
return lo;
}

4.3 哈希查找

// C++ STL
#include <unordered_map>
#include <unordered_set>
using namespace std;
unordered_map<string, int> dict;
dict["hello"] = 1;
if (dict.find("hello") != dict.end()) { /* ... */ }
unordered_set<int> seen;
if (seen.count(x)) { /* 已存在 */ }
seen.insert(x);
// ===== C 版:简易哈希表(拉链法)=====
#include <stdlib.h>
#include <string.h>
#define HASH_SIZE 1021 // 素数
typedef struct HashEntry {
char* key;
int value;
struct HashEntry* next;
} HashEntry;
typedef struct {
HashEntry* buckets[HASH_SIZE];
} HashMap;
unsigned int hash_func(const char* key) {
unsigned int hash = 5381; // djb2
int c;
while ((c = *key++))
hash = ((hash << 5) + hash) + c;
return hash % HASH_SIZE;
}
void hashmap_put(HashMap* map, const char* key, int value) {
unsigned int idx = hash_func(key);
HashEntry* entry = map->buckets[idx];
while (entry) {
if (strcmp(entry->key, key) == 0) {
entry->value = value;
return;
}
entry = entry->next;
}
// 新插入(头插法)
HashEntry* new_entry = (HashEntry*)malloc(sizeof(HashEntry));
new_entry->key = strdup(key);
new_entry->value = value;
new_entry->next = map->buckets[idx];
map->buckets[idx] = new_entry;
}
int hashmap_get(HashMap* map, const char* key, int* found) {
unsigned int idx = hash_func(key);
HashEntry* entry = map->buckets[idx];
while (entry) {
if (strcmp(entry->key, key) == 0) {
if (found) *found = 1;
return entry->value;
}
entry = entry->next;
}
if (found) *found = 0;
return -1;
}

经典:两数之和

#include <unordered_map>
#include <vector>
using namespace std;
vector<int> two_sum(vector<int>& nums, int target) {
unordered_map<int, int> seen;
for (int i = 0; i < nums.size(); i++) {
int complement = target - nums[i];
if (seen.count(complement))
return {seen[complement], i};
seen[nums[i]] = i;
}
return {};
}

4.4 字符串匹配 — KMP

// ===== C++ 版 =====
#include <string>
#include <vector>
using namespace std;
// 构建 next(前缀函数)数组
vector<int> build_next(const string& pattern) {
int m = pattern.size();
vector<int> nxt(m, 0);
for (int i = 1, j = 0; i < m; i++) {
while (j > 0 && pattern[i] != pattern[j])
j = nxt[j - 1];
if (pattern[i] == pattern[j])
j++;
nxt[i] = j;
}
return nxt;
}
int kmp_search(const string& text, const string& pattern) {
if (pattern.empty()) return 0;
vector<int> nxt = build_next(pattern);
int n = text.size(), m = pattern.size();
for (int i = 0, j = 0; i < n; i++) {
while (j > 0 && text[i] != pattern[j])
j = nxt[j - 1];
if (text[i] == pattern[j])
j++;
if (j == m)
return i - m + 1; // 匹配成功,返回起始位置
}
return -1;
}
// ===== C 版 =====
void compute_lps(char* pattern, int m, int* lps) {
int len = 0;
lps[0] = 0;
int i = 1;
while (i < m) {
if (pattern[i] == pattern[len]) {
len++;
lps[i] = len;
i++;
} else {
if (len != 0) {
len = lps[len - 1];
} else {
lps[i] = 0;
i++;
}
}
}
}
int kmp_search_c(char* text, char* pattern) {
int n = strlen(text);
int m = strlen(pattern);
if (m == 0) return 0;
int* lps = (int*)malloc(m * sizeof(int));
compute_lps(pattern, m, lps);
int i = 0, j = 0;
while (i < n) {
if (text[i] == pattern[j]) {
i++; j++;
}
if (j == m) {
free(lps);
return i - j; // 匹配到
}
if (i < n && text[i] != pattern[j]) {
if (j != 0)
j = lps[j - 1];
else
i++;
}
}
free(lps);
return -1;
}

5. 排序

5.1 排序算法总览

算法平均最坏空间稳定要点
冒泡O(n²)O(n²)O(1)相邻比较交换
选择O(n²)O(n²)O(1)每轮选最小放前面
插入O(n²)O(n²)O(1)类似打牌理牌
希尔O(n^1.3)O(n²)O(1)插入升级版
归并O(n log n)O(n log n)O(n)分治,先分后合
快速O(n log n)O(n²)O(log n)分区排序
O(n log n)O(n log n)O(1)建堆+反复弹出
计数O(n+k)O(n+k)O(n+k)值范围有限
基数O(d(n+k))O(d(n+k))O(n+k)按位排序

5.2 冒泡排序

// ===== C 版 =====
void bubble_sort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {
int swapped = 0;
for (int j = 0; j < n - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
swapped = 1;
}
}
if (!swapped) break; // 提前退出
}
}
// ===== C++ 版 =====
void bubble_sort(vector<int>& arr) {
int n = arr.size();
for (int i = 0; i < n - 1; i++) {
bool swapped = false;
for (int j = 0; j < n - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr[j], arr[j + 1]);
swapped = true;
}
}
if (!swapped) break;
}
}

5.3 选择排序

void selection_sort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {
int min_idx = i;
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[min_idx])
min_idx = j;
}
int tmp = arr[i];
arr[i] = arr[min_idx];
arr[min_idx] = tmp;
}
}

5.4 插入排序

// ===== C 版 =====
void insertion_sort(int arr[], int n) {
for (int i = 1; i < n; i++) {
int key = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
// C++ 用 STL:std::sort 对小块自动用插入排序

5.5 希尔排序

void shell_sort(int arr[], int n) {
for (int gap = n / 2; gap > 0; gap /= 2) {
for (int i = gap; i < n; i++) {
int temp = arr[i];
int j = i;
while (j >= gap && arr[j - gap] > temp) {
arr[j] = arr[j - gap];
j -= gap;
}
arr[j] = temp;
}
}
}

5.6 归并排序

// ===== C++ 版 =====
#include <vector>
using namespace std;
void merge(vector<int>& arr, int lo, int mid, int hi) {
vector<int> left(arr.begin() + lo, arr.begin() + mid + 1);
vector<int> right(arr.begin() + mid + 1, arr.begin() + hi + 1);
int i = 0, j = 0, k = lo;
while (i < left.size() && j < right.size()) {
if (left[i] <= right[j])
arr[k++] = left[i++];
else
arr[k++] = right[j++];
}
while (i < left.size()) arr[k++] = left[i++];
while (j < right.size()) arr[k++] = right[j++];
}
void merge_sort(vector<int>& arr, int lo, int hi) {
if (lo >= hi) return;
int mid = lo + (hi - lo) / 2;
merge_sort(arr, lo, mid);
merge_sort(arr, mid + 1, hi);
merge(arr, lo, mid, hi);
}
// ===== C 版 =====
void merge_c(int arr[], int lo, int mid, int hi) {
int n1 = mid - lo + 1;
int n2 = hi - mid;
int* left = (int*)malloc(n1 * sizeof(int));
int* right = (int*)malloc(n2 * sizeof(int));
for (int i = 0; i < n1; i++) left[i] = arr[lo + i];
for (int i = 0; i < n2; i++) right[i] = arr[mid + 1 + i];
int i = 0, j = 0, k = lo;
while (i < n1 && j < n2) {
if (left[i] <= right[j]) arr[k++] = left[i++];
else arr[k++] = right[j++];
}
while (i < n1) arr[k++] = left[i++];
while (j < n2) arr[k++] = right[j++];
free(left);
free(right);
}
void merge_sort_c(int arr[], int lo, int hi) {
if (lo >= hi) return;
int mid = lo + (hi - lo) / 2;
merge_sort_c(arr, lo, mid);
merge_sort_c(arr, mid + 1, hi);
merge_c(arr, lo, mid, hi);
}

归并排序的应用:逆序对计数

// 统计 arr[i] > arr[j] 且 i < j 的对数
long long merge_and_count(vector<int>& arr, int lo, int mid, int hi) {
vector<int> left(arr.begin() + lo, arr.begin() + mid + 1);
vector<int> right(arr.begin() + mid + 1, arr.begin() + hi + 1);
long long inv = 0;
int i = 0, j = 0, k = lo;
while (i < left.size() && j < right.size()) {
if (left[i] <= right[j]) {
arr[k++] = left[i++];
} else {
arr[k++] = right[j++];
inv += left.size() - i; // left[i:] 全部 > right[j]
}
}
while (i < left.size()) arr[k++] = left[i++];
while (j < right.size()) arr[k++] = right[j++];
return inv;
}
long long count_inversions(vector<int>& arr, int lo, int hi) {
if (lo >= hi) return 0;
int mid = lo + (hi - lo) / 2;
long long inv = count_inversions(arr, lo, mid);
inv += count_inversions(arr, mid + 1, hi);
inv += merge_and_count(arr, lo, mid, hi);
return inv;
}

5.7 快速排序

// ===== C 版:Lomuto 分区 =====
int partition(int arr[], int lo, int hi) {
int pivot = arr[hi]; // 选最后一个为 pivot
int i = lo; // i 指向第一个 > pivot 的位置
for (int j = lo; j < hi; j++) {
if (arr[j] <= pivot) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
i++;
}
}
int tmp = arr[i];
arr[i] = arr[hi];
arr[hi] = tmp;
return i;
}
void quick_sort(int arr[], int lo, int hi) {
if (lo >= hi) return;
int pivot_idx = partition(arr, lo, hi);
quick_sort(arr, lo, pivot_idx - 1);
quick_sort(arr, pivot_idx + 1, hi);
}
// ===== C++ 优化版:三数取中 + Hoare 分区 =====
void quick_sort_opt(vector<int>& arr, int lo, int hi) {
if (lo >= hi) return;
// 三数取中选 pivot
int mid = lo + (hi - lo) / 2;
if (arr[mid] < arr[lo]) swap(arr[lo], arr[mid]);
if (arr[hi] < arr[lo]) swap(arr[lo], arr[hi]);
if (arr[hi] < arr[mid]) swap(arr[mid], arr[hi]);
swap(arr[mid], arr[hi - 1]); // pivot 放 hi-1
int pivot = arr[hi - 1];
// Hoare 分区
int i = lo, j = hi - 1;
while (true) {
while (arr[++i] < pivot);
while (arr[--j] > pivot);
if (i >= j) break;
swap(arr[i], arr[j]);
}
swap(arr[i], arr[hi - 1]);
quick_sort_opt(arr, lo, i - 1);
quick_sort_opt(arr, i + 1, hi);
}

5.8 堆排序

// ===== C 版 =====
void heapify(int arr[], int n, int i) {
while (1) {
int largest = i;
int left = 2 * i + 1;
int right = 2 * i + 2;
if (left < n && arr[left] > arr[largest]) largest = left;
if (right < n && arr[right] > arr[largest]) largest = right;
if (largest == i) break;
int tmp = arr[i];
arr[i] = arr[largest];
arr[largest] = tmp;
i = largest;
}
}
void heap_sort(int arr[], int n) {
// 建大顶堆 O(n)
for (int i = n / 2 - 1; i >= 0; i--)
heapify(arr, n, i);
// 排序 O(n log n):堆顶和末尾交换,再下沉
for (int i = n - 1; i > 0; i--) {
int tmp = arr[0];
arr[0] = arr[i];
arr[i] = tmp;
heapify(arr, i, 0);
}
}
// ===== C++ 版:手写 =====
#include <algorithm>
#include <functional>
void heap_sort_stl(vector<int>& arr) {
make_heap(arr.begin(), arr.end()); // 大顶堆
sort_heap(arr.begin(), arr.end());
// 或者直接用 priority_queue
}

5.9 计数排序

void counting_sort(int arr[], int n) {
if (n <= 0) return;
// 找范围
int max_val = arr[0];
for (int i = 1; i < n; i++)
if (arr[i] > max_val) max_val = arr[i];
int* count = (int*)calloc(max_val + 1, sizeof(int));
for (int i = 0; i < n; i++)
count[arr[i]]++;
// 前缀和
for (int i = 1; i <= max_val; i++)
count[i] += count[i - 1];
// 倒序填入(保证稳定性)
int* output = (int*)malloc(n * sizeof(int));
for (int i = n - 1; i >= 0; i--) {
output[--count[arr[i]]] = arr[i];
}
for (int i = 0; i < n; i++)
arr[i] = output[i];
free(count);
free(output);
}

5.10 基数排序

void counting_sort_by_digit(int arr[], int n, int exp) {
int* output = (int*)malloc(n * sizeof(int));
int count[10] = {0};
for (int i = 0; i < n; i++)
count[(arr[i] / exp) % 10]++;
for (int i = 1; i < 10; i++)
count[i] += count[i - 1];
for (int i = n - 1; i >= 0; i--) {
int digit = (arr[i] / exp) % 10;
output[--count[digit]] = arr[i];
}
for (int i = 0; i < n; i++)
arr[i] = output[i];
free(output);
}
void radix_sort(int arr[], int n) {
if (n <= 0) return;
int max_val = arr[0];
for (int i = 1; i < n; i++)
if (arr[i] > max_val) max_val = arr[i];
for (int exp = 1; max_val / exp > 0; exp *= 10)
counting_sort_by_digit(arr, n, exp);
}

5.11 C++ STL 排序 — 直接用

#include <algorithm>
#include <vector>
using namespace std;
vector<int> arr = {5, 2, 8, 1, 9};
sort(arr.begin(), arr.end()); // 升序
sort(arr.begin(), arr.end(), greater<int>()); // 降序
sort(arr.begin(), arr.end(),
[](int a, int b) { return a > b; }); // 自定义
// 部分排序(前k个最小)
partial_sort(arr.begin(), arr.begin() + 5, arr.end());
// 第k大
nth_element(arr.begin(), arr.begin() + k, arr.end());
// 稳定排序
stable_sort(arr.begin(), arr.end());
// C 库
#include <stdlib.h>
int compare(const void* a, const void* b) {
return (*(int*)a - *(int*)b);
}
qsort(arr, n, sizeof(int), compare);

5.12 外部排序 — 数据太大内存放不下

/*
思路:多路归并
1. 把大文件切成 N 个能装入内存的块
2. 每块单独排序,写入临时文件
3. 用最小堆合并 N 个有序文件
*/
#include <queue>
#include <vector>
#include <fstream>
#include <string>
using namespace std;
// 简化版(每行一个整数)
void external_sort(const string& input_file, const string& output_file,
size_t chunk_size_mb = 100) {
// 1. 分块排序
ifstream fin(input_file);
vector<string> chunk_files;
vector<int> chunk;
size_t chunk_bytes = 0;
size_t max_bytes = chunk_size_mb * 1024 * 1024;
int val;
while (fin >> val) {
chunk.push_back(val);
chunk_bytes += sizeof(val) + 1; // 估计
if (chunk_bytes >= max_bytes) {
sort(chunk.begin(), chunk.end());
string tmp = input_file + ".tmp." + to_string(chunk_files.size());
ofstream fout(tmp);
for (int x : chunk) fout << x << "\n";
chunk_files.push_back(tmp);
chunk.clear();
chunk_bytes = 0;
}
}
// 最后一块
if (!chunk.empty()) {
sort(chunk.begin(), chunk.end());
string tmp = input_file + ".tmp." + to_string(chunk_files.size());
ofstream fout(tmp);
for (int x : chunk) fout << x << "\n";
chunk_files.push_back(tmp);
}
fin.close();
// 2. 多路归并
vector<ifstream*> readers;
for (auto& f : chunk_files)
readers.push_back(new ifstream(f));
// 堆中放 pair<value, reader_index>
using pii = pair<int, int>;
priority_queue<pii, vector<pii>, greater<pii>> heap;
for (int i = 0; i < readers.size(); i++) {
int x;
if ((*readers[i]) >> x)
heap.push({x, i});
}
ofstream fout(output_file);
while (!heap.empty()) {
auto [val, idx] = heap.top(); heap.pop();
fout << val << "\n";
int next;
if ((*readers[idx]) >> next)
heap.push({next, idx});
}
// 清理
for (auto& r : readers) { r->close(); delete r; }
for (auto& f : chunk_files) remove(f.c_str());
}

6. 刷题指引

6.1 树 / 二叉树

  • LeetCode 94, 144, 145 — 三种遍历(迭代+递归)
  • LeetCode 102, 103, 107 — 层序遍历
  • LeetCode 98 — 验证 BST
  • LeetCode 104 — 二叉树最大深度
  • LeetCode 236 — 最近公共祖先
  • LeetCode 105 — 前序+中序重建二叉树
  • LeetCode 297 — 序列化与反序列化二叉树

6.2 图

  • LeetCode 200 — 岛屿数量(DFS/BFS)
  • LeetCode 207 — 课程表(拓扑排序)
  • LeetCode 743 — 网络延迟时间(Dijkstra)
  • LeetCode 787 — K 站中转最便宜航班(Bellman-Ford)
  • LeetCode 133 — 克隆图

6.3 查找

  • LeetCode 1 — 两数之和(哈希表)
  • LeetCode 33 — 搜索旋转排序数组(二分)
  • LeetCode 34 — 查找第一个和最后一个位置(二分边界)
  • LeetCode 153 — 旋转排序数组最小值
  • LeetCode 4 — 两个有序数组的中位数(二分,hard)

6.4 排序

  • LeetCode 215 — 第 K 大元素(快选/堆)
  • LeetCode 912 — 排序数组
  • LeetCode 148 — 排序链表
  • LeetCode 179 — 最大数
  • LeetCode 315 — 计算右侧小于当前元素的个数

7. 几点真心话

  1. 先理解直觉,再记代码。 BST 中序遍历为什么有序?想清楚,比背代码重要一万倍。

  2. 手写代码。 看完觉得自己会了?关了教程,开个 .c 文件,用 gcc -g -Wall 编译,踩一遍所有坑。

  3. C 语言的坑。 malloc 后别忘 free。指针别野了。NULL 检查。int mid = lo + (hi - lo) / 2 防溢出。

  4. C++ 的甜头。 std::vectorstd::queuestd::priority_queuestd::unordered_map 这些 STL 能省你无数次 malloc 和手写栈。

  5. 复杂度要有直觉。 看到循环嵌套立即反应 O(n²),看到二分砍半就是 O(log n)。

  6. 堆和并查集是被低估的两把利器。 很多 hard 题就靠它们俩。

编译跑起来,别光看。