C++ list第二讲数据结构修炼:迭代器源码 + 栈队列适配器 + LeetCode 三道高频题


博主名称:deM0one ⭐️C++专栏: C++ ⭐️list第一讲: list第一讲

文章目录

  • [C++ 数据结构核心专题:从迭代器到栈、队列与二叉树遍历](#C++ 数据结构核心专题:从迭代器到栈、队列与二叉树遍历)
    • 一、引言
    • [二、C++ 迭代器深度解析](#二、C++ 迭代器深度解析)
      • [2.1 什么是迭代器](#2.1 什么是迭代器)
      • [2.2 自定义迭代器类实现](#2.2 自定义迭代器类实现)
      • [2.3 const_iterator 的设计](#2.3 const_iterator 的设计)
      • [2.4 使用示例](#2.4 使用示例)
      • [2.2 iterator 与 const_iterator 的区别](#2.2 iterator 与 const_iterator 的区别)
    • [三、栈(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:最小栈)
      • [5.1 题目描述](#5.1 题目描述)
      • [5.2 解题思路](#5.2 解题思路)
      • [5.3 C++ 实现](#5.3 C++ 实现)
      • [5.4 复杂度分析](#5.4 复杂度分析)
    • 六、栈的压入、弹出序列
      • [6.1 题目描述](#6.1 题目描述)
      • [6.2 示例](#6.2 示例)
      • [6.3 解题步骤](#6.3 解题步骤)
      • [6.4 C++ 实现框架](#6.4 C++ 实现框架)
    • [七、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 题目描述

设计一个支持 pushpoptop 操作,并能在常数时间内检索到最小元素的栈。

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 解题步骤

  1. 模拟压栈过程
  2. 每压入一个元素,检查是否与弹出序列当前值匹配
  3. 匹配则持续弹出,直到不匹配为止
  4. 最终栈为空则有效

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 数据结构专项训练
  • 《数据结构与算法分析》相关章节

任何的批评与建议欢迎指出,我们共同进步!

相关推荐
hehelm1 小时前
仿muduo库实现高并发服务器—EventLoop
linux·服务器·网络·c++
_Narcissus_1 小时前
常见数论算法笔记
数据结构·c++·算法·高精度·数论·快速幂·质数筛
余额瞒着我当琳1 小时前
C++STL--list底层实现,迭代器分类,模拟list的迭代器封装、实现
java·开发语言·c++
吴声子夜歌1 小时前
Java面试——数据结构(二)
java·数据结构·面试
kyle~1 小时前
计算机系统 --- 缓存一致性
开发语言·c++·缓存·计算机系统
octopus_c4 小时前
数据结构:堆(Heap)详解
c语言·数据结构·算法
吃着火锅x唱着歌10 小时前
Effective C++ 学习笔记 条款43 学习处理模板化基类内的名称
c++·笔记·学习
felixking11 小时前
C++20 协程
开发语言·c++·协程
小小龙学IT14 小时前
FlatBuffers 深度解析:Google 开源零拷贝序列化库完全指南
c++·开源