C++ 面试必考模块,笔试手撕、算法题、现场coding核心考点,链表/树/排序/查找为高频题型,标准答案直接背诵,代码模板可直接默写。
1. 链表(单链/双链)/ 循环链表
1.1 链表核心特性
- 单链表:节点含数据 + next 指针,内存离散,通过指针串联。
- 双链表:节点含 prev + next,支持双向遍历。
- 循环链表:尾节点 next 指向头节点,形成环。
1.2 链表 vs 数组
| 特性 | 链表 | 数组 |
|---|---|---|
| 存储 | 内存离散 | 内存连续 |
| 访问 | O(n) 顺序遍历 | O(1) 随机访问 |
| 增删 | O(1) 改指针 | O(n) 元素搬迁 |
| 内存 | 额外存指针 | 无额外开销 |
1.3 核心操作(手撕模板)
- 反转链表:
prev=null, curr=head, while(curr) { next=curr->next; curr->next=prev; prev=curr; curr=next; } - 链表环检测:快慢指针法 (fast=2*slow)
- 链表倒数第K个:双指针 (p1先走k步,p2同步)
1.4 高频面试题
- 反转链表:迭代/递归
- 链表环入口:快慢指针数学推导
- 合并两个有序链表:虚拟头节点
- 链表相交:双指针法
- 删除倒数第N个:双指针一次遍历
2. 二叉树(遍历 / 重建 / 高度)
2.1 二叉树遍历方式
- 前序:根-左-右
- 中序:左-根-右(排序树)
- 后序:左-右-根
- 层序:队列 + BFS
2.2 递归遍历模板
cpp
void traversal(Node* root) {
if (!root) return;
// 前序: visit(root)
traversal(root->left);
// 中序: visit(root)
traversal(root->right);
// 后序: visit(root)
}
2.3 核心算法
- 二叉树高度 :递归
height = max(height(left), height(right)) + 1 - 镜像翻转:递归交换左右子树
- 最近公共祖先:递归查找两节点,路径分叉处
- 重建二叉树:前序+中序 / 后序+中序
2.4 高频面试题
- 二叉树深度:递归
- 对称二叉树:递归比较
- 二叉树镜像:递归翻转
- 路径总和:回溯
- 层序遍历:队列实现
2.5 二叉搜索树 (BST)
- 左子树值 < 根值 < 右子树值
- 中序遍历得到有序序列
- 查找/插入/删除 O(log n)
3. 排序算法(八大排序)
3.1 时间复杂度汇总
| 排序 | 平均 | 最坏 | 稳定 | 场景 |
|---|---|---|---|---|
| 冒泡 | O(n²) | O(n²) | 稳定 | 教学 |
| 简单选择 | O(n²) | O(n²) | 不稳定 | 教学 |
| 简单插入 | O(n²) | O(n²) | 稳定 | 基本有序 |
| 希尔 | O(n^1.5) | O(n²) | 不稳定 | 进阶 |
| 堆排序 | O(nlogn) | O(nlogn) | 不稳定 | Top-K |
| 归并 | O(nlogn) | O(nlogn) | 稳定 | 外排序 |
| 快速 | O(nlogn) | O(n²) | 不稳定 | 最常用 |
| 计数 | O(n+k) | O(n+k) | 稳定 | 小范围整数 |
3.2 快排模板(手撕必背)
cpp
void quickSort(vector<int>& arr, int left, int right) {
if (left >= right) return;
int pivot = partition(arr, left, right);
quickSort(arr, left, pivot - 1);
quickSort(arr, pivot + 1, right);
}
int partition(vector<int>& arr, int left, int right) {
int p = arr[left]; // 基准
while (left < right) {
while (left < right && arr[right] >= p) right--;
arr[left] = arr[right];
while (left < right && arr[left] <= p) left++;
arr[right] = arr[left];
}
arr[left] = p;
return left;
}
3.3 归并排序(分治)
- 分解:递归二分数组
- 合并:双指针有序合并
- 稳定排序,适合外排序
3.4 堆排序
- 建大顶堆:父节点 >= 左右子节点
- 排序:堆顶与末尾交换,调节堆
- Top-K 问题最优解
4. 查找算法(二分/哈希)
4.1 二分查找
- 前提:有序数组
- 模板:
cpp
int binarySearch(vector<int>& arr, int target) {
int left = 0, right = arr.size() - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] == target) return mid;
else if (arr[mid] < target) left = mid + 1;
else right = mid - 1;
}
return -1;
}
- 边界:left < right vs left <= right
- 变形:查找左边界、查找右边界、旋转数组
4.2 二分答案
- 有单调性 monotone Function 可用二分
- 求最小值/最大值/满足条件的最优解
4.3 哈希查找
- unordered_map O(1) 平均
- 哈希冲突:链地址法
- 再哈希:扩容时重新计算
4.4 面试追问
- 为什么二分用 left + (right-left)/2? 防溢出
- 二分查找 vs 二叉搜索树? 均logN,树更均衡
5. 位运算(常用技巧)
5.1 常用公式
n & (n-1):消除最低位的1n & (-n):取最低位的1n ^ n = 0:消元n | 0 = n~n + 1 = -n
5.2 代码模板
cpp
// 判断奇偶
if (n & 1) // n是奇数
// 消除最低位的1
n &= (n-1)
// 获取最低位的1
int lowestBit = n & (-n)
// 判断是否为2的幂
n > 0 && (n & (n-1)) == 0
5.3 应用场景
- 统计1的个数(布隆过滤器)
- 交换两数(不加临时变量)
- 找出缺失的整数
6. 栈与队列(应用/设计)
6.1 栈应用
- 函数调用栈:递归实现
- 括号匹配:栈顶匹配
- 前缀/中缀/后缀表达式:运算符优先级
- 单调栈:求下一个更大元素
6.2 队列应用
- BFS 层序遍历
- 滑动窗口最大值:双端队列
- 生产者-消费者
6.3 队设计
- 两个栈实现队列
- 两个队列实现栈
6.4 模板
cpp
// 单调栈 - 下一个更大元素
vector<int> nextGreater(vector<int>& nums) {
vector<int> res(nums.size(), -1);
stack<int> st;
for (int i = 0; i < nums.size(); i++) {
while (!st.empty() && nums[i] > nums[st.top()]) {
res[st.top()] = nums[i];
st.pop();
}
st.push(i);
}
return res;
}
7. Top-K 问题
7.1 解法对比
| 方法 | 时间 | 空间 | 特点 |
|---|---|---|---|
| 排序 | O(nlog n) | O(1) | 简单粗暴 |
| 堆 | O(nlog k) | O(k) | 海量数据 |
| 快速选择 | O(n) | O(1) | 平均最快 |
| 计数排序 | O(n+k) | O(k) | 小范围整数 |
7.2 堆模板(手撕)
cpp
// 小顶堆 Top-K
priority_queue<int, vector<int>, greater<int>> pq;
for (int x : nums) {
pq.push(x);
if (pq.size() > k) pq.pop();
}
7.3 TOP-K 变形
- K个最小/最大
- 中位数(第k大/小)
- 频率最高的K个
8. 回溯与 DFS/BFS
8.1 DFS 模板
cpp
void dfs(Node* node) {
if (visited[node]) return;
visited[node] = true;
// visit(node)
for (auto next : node.neighbors) {
if (!visited[next]) dfs(next);
}
}
8.2 回溯模板
cpp
void backtrack(vector<int>& path, ...) {
if (满足条件) {
result.push_back(path);
return;
}
for (选择 : candidates) {
path.push_back(选择);
backtrack(path, ...);
path.pop_back(); // 回溯
}
}
8.3 经典问题
- 全排列
- 子集/组合
- N皇后
- 岛屿数量
- 路径总和
8.4 BFS 模板
cpp
queue<Node*> q;
q.push(start);
while (!q.empty()) {
Node* cur = q.front(); q.pop();
for (auto next : cur.neighbors) {
if (!visited[next]) {
visited[next] = true;
q.push(next);
}
}
}
9. 动态规划(DP)
9.1 DP 四步曲
- 定义状态:dpi 含义
- 状态转移:dpi = f(dpi-1)
- 初始化:dp0、dp1
- 遍历顺序:从前向后
9.2 经典题目模板
cpp
// 斐波那契
dp[0]=0, dp[1]=1;
for (int i=2; i<=n; i++)
dp[i] = dp[i-1] + dp[i-2];
// 最长上升子序列
for (int i=1; i<n; i++)
for (int j=0; j<i; j++)
if (nums[i] > nums[j])
dp[i] = max(dp[i], dp[j]+1);
// 背包问题
for (int i=0; i<n; i++)
for (int j=W; j>=w[i]; j--)
dp[j] = max(dp[j], dp[j-w[i]]+v[i]);
9.3 经典问题
- 斐波那契数列
- 爬楼梯
- 打家劫舍
- 最长上升子序列 LCS
- 编辑距离
- 背包问题
10. 双指针与滑动窗口
10.1 双指针
- 对撞指针:左右逼近,有序数组
- 快慢指针:链表判环、删除重复
10.2 滑动窗口
cpp
int slidingWindow(string s, string t) {
unordered_map<char, int> need, window;
for (char c : t) need[c]++;
int left = 0, right = 0;
int valid = 0;
while (right < s.size()) {
char c = s[right++];
// 扩大窗口
if (need.count(c)) {
window[c]++;
if (window[c] == need[c]) valid++;
}
// 收缩窗口
while (valid == need.size()) {
// 更新答案
char d = s[left++];
if (need.count(d)) {
if (window[d] == need[d]) valid--;
window[d]--;
}
}
}
}
10.3 应用
- 有序数组两数之和
- 移除重复元素
- 最大不重复子串长度
- 最短覆盖子串
🔥 本章综合高频追问
-
问 :快排为什么不稳定?
答:基准选择可能导致相同元素顺序变化。
-
问 :为什么哈希表不稳定?
答:哈希碰撞,红黑树保证O(logN)。
-
问 :DFS vs BFS 如何选?
答:找最短路径用BFS,其他可用DFS。
-
问 :动态规划如何确定状态?
答:最后一步/最值问题/是否存在。
📝 模块总结
本模块覆盖 C++ 面试手撕代码核心题型,链表、二叉树、排序、查找、DP 为必考内容。全部理解并能快速默写,可应对90%算法 coding 面。
