
博主名称:deM0one ⭐️C++专栏: C++ ⭐️list第一讲: list第一讲
文章目录
- [C++ 数据结构核心专题:从迭代器到栈、队列与二叉树遍历](#C++ 数据结构核心专题:从迭代器到栈、队列与二叉树遍历)
-
- 一、引言
- [二、C++ 迭代器深度解析](#二、C++ 迭代器深度解析)
- [三、栈(Stack)------ LIFO 的艺术](#三、栈(Stack)—— LIFO 的艺术)
-
- [3.1 栈的定义与特性](#3.1 栈的定义与特性)
- [3.2 成员函数速查表](#3.2 成员函数速查表)
- [3.3 应用场景](#3.3 应用场景)
- [四、队列(Queue)------ FIFO 的哲学](#四、队列(Queue)—— FIFO 的哲学)
-
- [4.1 队列的定义与特性](#4.1 队列的定义与特性)
- [4.2 成员函数速查表](#4.2 成员函数速查表)
- [4.3 与栈的对比](#4.3 与栈的对比)
- [五、LeetCode 155:最小栈](#五、LeetCode 155:最小栈)
- 六、栈的压入、弹出序列
- [七、LeetCode 102:二叉树的层序遍历](#七、LeetCode 102:二叉树的层序遍历)
-
- [7.1 题目描述](#7.1 题目描述)
- [7.2 二叉树示例](#7.2 二叉树示例)
- [7.3 层序遍历过程图解](#7.3 层序遍历过程图解)
- [7.4 C++ 实现](#7.4 C++ 实现)
- [7.5 算法要点](#7.5 算法要点)
- 八、总结与扩展
-
- [8.1 核心知识点回顾](#8.1 核心知识点回顾)
- [8.2 后续学习建议](#8.2 后续学习建议)
- [8.3 参考资源](#8.3 参考资源)
C++ 数据结构核心专题:从迭代器到栈、队列与二叉树遍历
一、引言
- 数据结构是程序员的基石
- 本文聚焦 STL 容器适配器与经典算法题
- 适合有一定 C++ 基础的同学进阶学习
二、C++ 迭代器深度解析
2.1 什么是迭代器
- 迭代器的本质:泛型指针
- 统一访问容器元素的方式
2.2 自定义迭代器类实现
cpp
template<class T, class Ref, class Ptr>
class _list_iterator {
public:
T* _node;
Ref operator*() { return _node->_data; }
Ptr operator->() { return &_node->_data; }
_list_iterator& operator++() {
_node = _node->_next;
return *this;
}
bool operator!=(const _list_iterator& it) const {
return _node != it._node;
}
};
2.3 const_iterator 的设计
- 为什么需要两份迭代器?
- 返回值类型控制访问权限
const_iterator返回const T&,禁止修改
2.4 使用示例
cpp
list<int>::iterator ita = lst.begin();
while (ita != lst.end()) {
cout << ita->a1 << " " << ita->a2 << endl;
++ita;
}
2.2 iterator 与 const_iterator 的区别
核心差异
| 特性 | iterator | const_iterator |
|---|---|---|
| 能否修改元素 | ✅ 可以 | ❌ 不可以 |
| 适用场景 | 需要修改容器内容 | 只读遍历 |
函数重载示例
cpp
// const 对象调用返回 const_iterator
const_iterator begin() const { return _head->next; }
// 普通对象调用返回 iterator
iterator begin() { return _head->next; }
设计思想:保证常量对象的安全性,同时允许遍历。
三、栈(Stack)------ LIFO 的艺术
3.1 栈的定义与特性
- LIFO(Last In First Out)后进先出
- 元素只能从一端(栈顶)插入和删除
- 容器适配器:基于 deque/vector 实现
3.2 成员函数速查表
| 函数 | 功能 |
|---|---|
push() |
将元素入栈 |
pop() |
弹出栈顶元素 |
top() |
返回栈顶元素引用 |
empty() |
判断栈是否为空 |
size() |
返回栈中元素个数 |
emplace() |
原地构造并插入元素 |
swap() |
交换两个栈的内容 |
3.3 应用场景
- 表达式求值
- 括号匹配
- 函数调用栈
- 回文判断
四、队列(Queue)------ FIFO 的哲学
4.1 队列的定义与特性
- FIFO(First In First Out)先进先出
- 一端入队(rear),一端出队(front)
- 同样基于容器适配器实现
4.2 成员函数速查表
| 函数 | 功能 |
|---|---|
push() |
将元素入队 |
pop() |
弹出队首元素 |
front() |
返回队首元素引用 |
back() |
返回队尾元素引用 |
empty() |
判断队列是否为空 |
size() |
返回队列中元素个数 |
emplace() |
原地构造并插入元素 |
4.3 与栈的对比
- 入队和出队端不同
- 适用场景差异
- BFS 遍历时队列的天然优势
五、LeetCode 155:最小栈
5.1 题目描述
设计一个支持 push、pop、top 操作,并能在常数时间内检索到最小元素的栈。
5.2 解题思路
- 双栈法:数据栈 + 辅助最小栈
- 辅助栈始终记录当前最小值
5.3 C++ 实现
cpp
class MinStack {
stack<int> _st;
stack<int> _minst;
public:
void push(int val) {
_st.push(val);
if (_minst.empty() || val <= _minst.top()) {
_minst.push(val);
}
}
void pop() {
if (_st.top() == _minst.top()) {
_minst.pop();
}
_st.pop();
}
int top() {
return _st.top();
}
int getMin() {
return _minst.top();
}
};
执行流程图解
操作序列:push(-2), push(0), push(-3), getMin(), pop(), top(), getMin()
push(-2):
_st: [-2]
_min_st: [-2]
push(0):
_st: [-2, 0]
_min_st: [-2, -2] // 0 > -2,复制栈顶
push(-3):
_st: [-2, 0, -3]
_min_st: [-2, -2, -3] // -3 < -2,压入 -3
getMin(): 返回 -3
pop():
_st: [-2, 0]
_min_st: [-2, -2]
top(): 返回 0
getMin(): 返回 -2
测试用例
输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]
输出:
[null,null,null,null,-3,null,0,-2]
5.4 复杂度分析
- 时间复杂度:所有操作 O(1)
- 空间复杂度:O(n),最坏情况下两个栈元素相同
六、栈的压入、弹出序列
6.1 题目描述
输入两个整数序列,第一个序列表示栈的压入顺序,判断第二个序列是否可能为该栈的弹出顺序。
6.2 示例
输入: pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
输出: true
解释: push(1), push(2), push(3), push(4), pop()->4,
push(5), pop()->5, pop()->3, pop()->2, pop()->1
输入: pushed = [1,2,3,4,5], popped = [4,3,5,1,2]
输出: false
解释: 1 不能在 2 之前弹出
示例分析
压入序列:[1, 2, 3, 4, 5]
弹出序列 A:[4, 5, 3, 2, 1] → 合法 ✅
弹出序列 B:[4, 3, 5, 1, 2] → 不合法 ❌
6.3 解题步骤
- 模拟压栈过程
- 每压入一个元素,检查是否与弹出序列当前值匹配
- 匹配则持续弹出,直到不匹配为止
- 最终栈为空则有效
6.4 C++ 实现框架
cpp
bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
stack<int> st;
int j = 0;
for (int x : pushed) {
st.push(x);
while (!st.empty() && j < popped.size() && st.top() == popped[j]) {
st.pop();
j++;
}
}
return st.empty();
}
执行流程图解
案例 A:4, 5, 3, 2, 1
压入 1, 2, 3, 4 → 栈:[1, 2, 3, 4]
弹出 4, 3 → 栈:[1, 2]
压入 5 → 栈:[1, 2, 5]
弹出 5, 2, 1 → 栈:[]
返回 true ✅
案例 B:4, 3, 5, 1, 2
压入 1, 2, 3, 4 → 栈:[1, 2, 3, 4]
弹出 4, 3 → 栈:[1, 2]
压入 5 → 栈:[1, 2, 5]
栈顶 5 ≠ 1 → 无法继续弹出
返回 false ❌
七、LeetCode 102:二叉树的层序遍历
7.1 题目描述
给你二叉树的根节点 root,返回其节点值的层序遍历(逐层地,从左到右访问所有节点)。
7.2 二叉树示例
3
/ \
9 20
/ \
15 7
/
21
7.3 层序遍历过程图解
| 步骤 | 队列状态 | levelSize | 本层输出 |
|---|---|---|---|
| 初始 | {3} | 1 | 3 |
| 第一层后 | {9, 20} | 2 | 9, 20 |
| 第二层后 | {15, 7, 21} | 3 | 15, 7, 21 |
7.4 C++ 实现
cpp
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> ret;
if (!root) return ret;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
int currentLevelSize = q.size();
ret.push_back(vector<int>());
for (int i = 0; i < currentLevelSize; ++i) {
auto node = q.front();
q.pop();
ret.back().push_back(node->val);
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
}
return ret;
}
};
执行流程图解
初始:q = [3]
第一层(size=1):
弹出 3 → 加入 [3]
压入 9, 20 → q = [9, 20]
res = [[3]]
第二层(size=2):
弹出 9 → 加入 [9](无子节点)
弹出 20 → 加入 [20]
压入 15, 7 → q = [15, 7]
res = [[3], [9, 20]]
第三层(size=2):
弹出 15 → 加入 [15](无子节点)
弹出 7 → 加入 [7](无子节点)
res = [[3], [9, 20], [15, 7]]
队列为空,结束 ✅
7.5 算法要点
- 使用 BFS(广度优先搜索) 配合队列
- 每层记录节点数量
currentLevelSize - 内层循环恰好处理当前层所有节点
- 将子节点加入队列,为下一层做准备
八、总结与扩展
8.1 核心知识点回顾
- 迭代器:连接算法与容器的桥梁
- 栈:LIFO,适用于回溯、匹配类问题
- 队列:FIFO,适用于 BFS、调度类问题
- 最小栈:空间换时间的经典技巧
- 层序遍历:BFS 的标准应用
8.2 后续学习建议
- 尝试实现其他容器适配器(priority_queue)
- 练习二叉树的其他遍历方式(前序、中序、后序)
- 探索深度优先搜索(DFS)与 BFS 的对比
8.3 参考资源
- C++ STL 官方文档
- LeetCode 数据结构专项训练
- 《数据结构与算法分析》相关章节
任何的批评与建议欢迎指出,我们共同进步!