C++栈与队列进阶实战

C++ 链表、栈、队列 一、C++ 链表 链表实际上是线性表的链式存储结构,与数组不同的是,它是用一组任意的存储单元来存储线性表中的数据,存储单元不一定是连续的, 且链表的长度不是固定的,链表数据的这一特点使其可以非常的方便地实现节点的插入和删除操作 1.1 几种链表的介绍 1.1.1 单向链表 单向链表很容易理解,就说把指针当成一条链一样连接每一个节点,每个节点包含了一个或者多个数据。链表的存储单元不一定是连续的,长度不固定,主要用于方便实现节点的插入和删除操作。官方的话这里就不说了,作为数据结构里最基本的,感兴趣的童鞋自. 阅读详情

目录

一、引言

栈(Stack)和队列(Queue)是两种最基础的数据结构,它们分别遵循 LIFO(后进先出)和 FIFO(先进先出)的原则。在 C++ STL 中,std::stack 和 std::queue 被设计为容器适配器(Container Adapter),默认底层基于 std::deque 实现。

但是,仅仅知道 push()、pop()、top() 这些基本操作是远远不够的。真正体现功力的,是能否在复杂的实际场景中灵活运用栈和队列的核心思想来解决难题。

今天这篇文章将会深入两个经典的进阶应用场景:

  1. 逆波兰表达式计算器 :栈在表达式求值中的经典运用,涵盖中缀转后缀、优先级处理、括号嵌套等硬核知识点;
  2. 反向迭代器的设计与实现 :迭代器适配器模式的精妙体现,通过分析 SGI-STL 源码,手把手带你实现一个通用的反向迭代器。

读完这篇文章,你不仅能够彻底搞懂计算器和反向迭代器的实现原理,更能深入理解 STL 的设计哲学,真正做到举一反三。


二、栈的经典应用:逆波兰表达式与计算器实现

2.1 表达式的前世今生:中缀、前缀与后缀

我们日常书写的数学表达式都是中缀表达式(Infix Notation),即运算符在两个运算数中间:

1 - 2 * (3 - 4) + 5

这种写法对人来说直观易懂,但对计算机来说却是个麻烦——你从左往右读,遇到 - 和 * 都不能马上计算,因为后面可能有括号优先级更高。

为了解决这个问题,波兰逻辑学家 J·卢卡西维兹(Jan Łukasiewicz)在 1929 年提出了逆波兰表示法(Reverse Polish Notation, RPN),也就是后缀表达式(Postfix Notation)。

三种表达式对比:

类型别称格式示例
中缀表达式Infix运算符在中间1 + 2
前缀表达式波兰表达式(PN)运算符在前面+ 1 2
后缀表达式逆波兰表达式(RPN)运算符在后面1 2 +

后缀表达式的核心优势:它天然去除了括号和优先级的困扰!因为表达式本身的排列顺序就已经确定了运算次序,计算机可以严格从左到右读取并计算,不需要回溯和优先级判断。

🔗 推荐练习:LeetCode 150. 逆波兰表达式求值 和 LeetCode 224. 基本计算器

2.2 后缀表达式求值——栈的天然舞台

后缀表达式的求值算法出奇地简洁——这正是栈结构大显身手的地方。

算法思路

  1. 建立一个栈,用于存储运算数;
  2. 从左到右依次读取后缀表达式中的每个元素:
    • 如果是运算数,直接入栈;
    • 如果是运算符,从栈顶弹出两个运算数(先出栈的是右操作数,后出栈的是左操作数),进行计算,将结果压回栈中;
  3. 读取结束后,栈中剩下的唯一元素就是最终结果。

画个图理解一下:假设后缀表达式为 ["2", "1", "+", "3", "*"],即中缀的 (2+1)*3:

读 "2"  →  栈: [2]
读 "1"  →  栈: [2, 1]
读 "+"  →  弹出 1, 2 → 2+1=3 → 栈: [3]
读 "3"  →  栈: [3, 3]
读 "*"  →  弹出 3, 3 → 3*3=9 → 栈: [9]
结果 = 9 ✅

代码实现

class Solution {
public:
    int evalRPN(const vector<string>& tokens) {
        stack<int> s;
        for (size_t i = 0; i < tokens.size(); ++i) {
            const string& str = tokens[i];
            // 不是运算符 → 是运算数,入栈
            if (!("+" == str || "-" == str || "*" == str || "/" == str)) {
                s.push(stoi(str));
            } else {
                // 是运算符 → 弹出两个运算数进行计算
                int right = s.top();
                s.pop();
                int left = s.top();
                s.pop();
                switch (str[0]) {
                    case '+': s.push(left + right); break;
                    case '-': s.push(left - right); break;
                    case '*': s.push(left * right); break;
                    case '/': s.push(left / right); break;
                }
            }
        }
        return s.top();
    }
};

这段代码清晰展示了栈在表达式求值中的核心作用:暂存操作数,按需取出计算,结果继续参与后续运算。


2.3 中缀转后缀——算法的核心难点

后缀表达式求值很简单,但问题在于——用户输入的永远是中缀表达式。如何将中缀表达式转换成后缀表达式,才是整个计算器实现中最考验思维的部分。

转换思路详解

我们逐条梳理转换规则:

规则一:遇到运算数,直接输出。

这个好理解,运算数的相对顺序在中缀和后缀表达式中是不变的。

规则二:建立一个栈存储运算符,遇到运算符时与栈顶比较优先级。

  • 如果栈为空,或者当前运算符优先级 > 栈顶运算符优先级,则将当前运算符入栈。

    • 为什么?因为栈里存的是"之前的运算符",如果当前运算符优先级更高,说明前面的还不能算,当前的也不能算(后面可能还有更高优先级的),所以先存着。
  • 如果当前运算符优先级 ≤ 栈顶运算符优先级,则说明栈顶的运算符已经可以计算了。此时,将栈顶运算符出栈并输出,然后继续用当前运算符与新的栈顶比较(这就是一个循环/递归判断的过程)。

规则三:遇到括号,递归处理子表达式。

将括号内的内容视为一个独立的子表达式,递归调用转换函数。遇到 ) 时,将栈中剩余运算符全部输出,然后返回。

规则四:表达式读取结束后,将栈中剩余运算符全部输出。

运算符优先级

对于四则运算,我们定义两级优先级:

运算符优先级
+ -1
* /2

代码实现

class Solution {
public:
    // 获取运算符优先级
    int operatorPrecedence(char ch) {
        struct OpPD {
            char _op;
            int  _pd;
        };
        static OpPD arr[] = { 
            { '+', 1 }, { '-', 1 }, 
            { '*', 2 }, { '/', 2 } 
        };
        for (auto& e : arr) {
            if (e._op == ch) {
                return e._pd;
            }
        }
        assert(false);
        return -1;
    }

    // 中缀表达式 → 后缀表达式(RPN)
    // s: 原始中缀表达式字符串
    // i: 当前处理的字符下标(引用传递,保证递归时位置同步)
    // v: 输出的后缀表达式 token 数组
    void toRPN(const string& s, size_t& i, vector<string>& v) {
        stack<char> st;  // 运算符栈
        while (i < s.size()) {
            if (isdigit(s[i])) {
                // === 情况1: 遇到运算数,直接输出 ===
                string num;
                while (i < s.size() && isdigit(s[i])) {
                    num += s[i];
                    ++i;
                }
                v.push_back(num);
            } else {
                if (s[i] == '(') {
                    // === 情况2: 遇到左括号,递归处理子表达式 ===
                    ++i;
                    toRPN(s, i, v);
                } else if (s[i] == ')') {
                    // === 情况3: 遇到右括号,输出栈中所有运算符,结束递归 ===
                    ++i;
                    while (!st.empty()) {
                        v.push_back(string(1, st.top()));
                        st.pop();
                    }
                    return;
                } else {
                    // === 情况4: 遇到运算符,比较优先级 ===
                    if (st.empty() || 
                        operatorPrecedence(s[i]) > operatorPrecedence(st.top())) {
                        // 当前优先级更高 → 入栈
                        st.push(s[i]);
                        ++i;
                    } else {
                        // 栈顶优先级更高或相等 → 栈顶出栈输出
                        v.push_back(string(1, st.top()));
                        st.pop();
                        // 注意:这里不自增 i,当前运算符需要继续和新的栈顶比较!
                    }
                }
            }
        }
        // === 表达式结束,输出栈中所有运算符 ===
        while (!st.empty()) {
            v.push_back(string(1, st.top()));
            st.pop();
        }
    }
};

关键细节:遇到运算符时,如果当前优先级不高于栈顶,出栈输出后不自增 i。这是因为当前运算符需要继续和新的栈顶进行比较——这是一个最容易出错的地方,一定要注意!


2.4 完整计算器——把所有零件组装起来

有了 evalRPN 和 toRPN 两个核心函数,我们已经能处理标准的四则运算表达式了。但在实际 OJ 题目中,还有两个隐藏的坑需要填:

  1. 空格处理:输入字符串可能包含空格,需要统一去除;
  2. 负数与减号的区分:-3 + 2 和 5 - 3 中的 - 含义完全不同。

解决方案:将所有表示负数的 -x 转换为 0-x,这样负数就变成了一个合法的"0减x"表达式。

判断条件:如果 - 出现在字符串开头,或者 - 前面的字符不是数字且不是 ),那它就是负号。

完整代码

class Solution {
public:
    // 获取运算符优先级
    int operatorPrecedence(char ch) {
        struct OpPD {
            char _op;
            int  _pd;
        };
        static OpPD arr[] = { 
            { '+', 1 }, { '-', 1 }, 
            { '*', 2 }, { '/', 2 } 
        };
        for (auto& e : arr) {
            if (e._op == ch) return e._pd;
        }
        assert(false);
        return -1;
    }

    // 中缀表达式转后缀表达式(RPN)
    void toRPN(const string& s, size_t& i, vector<string>& v) {
        stack<char> st;
        while (i < s.size()) {
            if (isdigit(s[i])) {
                string num;
                while (i < s.size() && isdigit(s[i])) {
                    num += s[i];
                    ++i;
                }
                v.push_back(num);
            } else {
                if (s[i] == '(') {
                    ++i;
                    toRPN(s, i, v);          // 递归处理子表达式
                } else if (s[i] == ')') {
                    ++i;
                    while (!st.empty()) {
                        v.push_back(string(1, st.top()));
                        st.pop();
                    }
                    return;
                } else {
                    if (st.empty() || 
                        operatorPrecedence(s[i]) > operatorPrecedence(st.top())) {
                        st.push(s[i]);
                        ++i;
                    } else {
                        v.push_back(string(1, st.top()));
                        st.pop();
                    }
                }
            }
        }
        while (!st.empty()) {
            v.push_back(string(1, st.top()));
            st.pop();
        }
    }

    // 后缀表达式求值
    int evalRPN(const vector<string>& tokens) {
        stack<int> s;
        for (size_t i = 0; i < tokens.size(); ++i) {
            const string& str = tokens[i];
            if (!("+" == str || "-" == str || "*" == str || "/" == str)) {
                s.push(stoi(str));
            } else {
                int right = s.top(); s.pop();
                int left  = s.top(); s.pop();
                switch (str[0]) {
                    case '+': s.push(left + right); break;
                    case '-': s.push(left - right); break;
                    case '*': s.push(left * right); break;
                    case '/': s.push(left / right); break;
                }
            }
        }
        return s.top();
    }

    // 完整计算器入口
    int calculate(string s) {
        // Step 1: 去除所有空格
        std::string news;
        news.reserve(s.size());
        for (auto ch : s) {
            if (ch != ' ') news += ch;
        }
        s.swap(news);
        news.clear();

        // Step 2: 将负数 "-x" 转换为 "0-x"
        for (size_t i = 0; i < s.size(); ++i) {
            if (s[i] == '-' && 
                (i == 0 || (!isdigit(s[i - 1]) && s[i - 1] != ')'))) {
                news += "0-";   // 负号 → 补 0
            } else {
                news += s[i];
            }
        }

        // Step 3: 中缀 → 后缀
        size_t i = 0;
        vector<string> v;
        toRPN(news, i, v);

        // Step 4: 后缀求值
        return evalRPN(v);
    }
};

执行流程示例

以 "1-2*(3-4)+5" 为例(这是一个非常经典的测试用例):

Step 1: 去空格      → "1-2*(3-4)+5"
Step 2: 处理负数    → "1-2*(3-4)+5"  (没有负数,不变)
Step 3: 中缀转后缀  → ["1","2","3","4","-","*","-","5","+"]
Step 4: 后缀求值
  1         → 栈: [1]
  2         → 栈: [1, 2]
  3         → 栈: [1, 2, 3]
  4         → 栈: [1, 2, 3, 4]
  -         → 3-4=-1     → 栈: [1, 2, -1]
  *         → 2*(-1)=-2  → 栈: [1, -2]
  -         → 1-(-2)=3   → 栈: [3]
  5         → 栈: [3, 5]
  +         → 3+5=8      → 栈: [8]
结果: 8 ✅

🎯 小结:这个计算器实现充分利用了栈的后进先出特性——运算符栈保证了优先级高的运算符先出栈被计算;运算数栈保证了计算时先弹出的作为右操作数、后弹出的作为左操作数。栈在这里不仅仅是一个数据结构,更是整个算法正确性的基石。


三、反向迭代器:迭代器适配器模式的精妙运用

3.1 从一个简单需求说起

假设你有一个 vector<int> v = {1, 2, 3, 4},现在你想从后往前遍历它。STL 给了我们现成的工具:

vector<int> v = {1, 2, 3, 4};
// 使用反向迭代器从后往前遍历
for (auto rit = v.rbegin(); rit != v.rend(); ++rit) {
    cout << *rit << " ";  // 输出: 4 3 2 1
}

看起来很简单对吧?但是你有没有想过——反向迭代器到底是怎么实现的? rbegin() 返回的是什么?为什么 ++rit 实际上在往"前"走?

这些问题的答案,正是适配器设计模式(Adapter Pattern)在 STL 中的经典应用。反向迭代器本质上是一个迭代器适配器(Iterator Adapter),它包装(wrap)了一个正向迭代器,通过对正向迭代器的操作进行"反向"映射,实现了反向遍历的效果。

3.2 SGI-STL 源码探秘——站在巨人的肩膀上

我们以 SGI-STL 30 版本源码为例,来一探究竟。反向迭代器的核心实现位于 stl_iterator.h 中。

容器如何使用反向迭代器

首先看看 list 和 vector 是如何定义反向迭代器的:

// ==================== stl_list.h ====================
template <class T, class Alloc = alloc>
class list {
public:
    typedef __list_iterator<T, T&, T*>             iterator;
    typedef __list_iterator<T, const T&, const T*> const_iterator;

#ifdef __STL_CLASS_PARTIAL_SPECIALIZATION
    // 偏特化版本:借助 iterator_traits 萃取类型信息
    typedef reverse_iterator<const_iterator> const_reverse_iterator;
    typedef reverse_iterator<iterator>       reverse_iterator;
#else
    // 旧版本:需要显式传递数据类型
    typedef reverse_bidirectional_iterator<const_iterator, value_type,
        const_reference, difference_type>    const_reverse_iterator;
    typedef reverse_bidirectional_iterator<iterator, value_type,
        reference, difference_type>          reverse_iterator;
#endif

    // ⚠️ 注意这个关键设计!!!
    reverse_iterator rbegin() { return reverse_iterator(end()); }
    reverse_iterator rend()   { return reverse_iterator(begin()); }
};
// ==================== stl_vector.h ====================
template <class T, class Alloc = alloc>
class vector {
public:
    typedef value_type* iterator;
    // ... 类似结构的反向迭代器定义 ...

    // ⚠️ 同样的对称设计!!!
    reverse_iterator rbegin() { return reverse_iterator(end()); }
    reverse_iterator rend()   { return reverse_iterator(begin()); }
};

关键发现:rbegin() 封装的是 end() 位置的迭代器,rend() 封装的是 begin() 位置的迭代器!这是一种巧妙的对称设计。

reverse_iterator 的核心源码

// ==================== stl_iterator.h ====================
template <class Iterator>
class reverse_iterator {
protected:
    Iterator current;  // 被包装的正向迭代器
public:
    // 类型萃取 —— 从 Iterator 中提取各种类型信息
    typedef typename iterator_traits<Iterator>::iterator_category  iterator_category;
    typedef typename iterator_traits<Iterator>::value_type         value_type;
    typedef typename iterator_traits<Iterator>::difference_type    difference_type;
    typedef typename iterator_traits<Iterator>::pointer            pointer;
    typedef typename iterator_traits<Iterator>::reference          reference;

    typedef Iterator                    iterator_type;
    typedef reverse_iterator<Iterator>  self;

public:
    // 构造函数
    reverse_iterator() {}
    explicit reverse_iterator(iterator_type x) : current(x) {}
    reverse_iterator(const self& x) : current(x.current) {}

    // 获取底层正向迭代器的 base()
    iterator_type base() const { return current; }

    // ===== ⚠️ 最核心、最反直觉的部分!!! =====
    reference operator*() const {
        Iterator tmp = current;
        return *--tmp;    // 解引用的是 current 的【前一个】位置!
    }

    pointer operator->() const { 
        return &(operator*()); 
    }

    // operator++ 实际在底层做 --current
    self& operator++() {
        --current;
        return *this;
    }
    self operator++(int) {
        self tmp = *this;
        --current;
        return tmp;
    }

    // operator-- 实际在底层做 ++current
    self& operator--() {
        ++current;
        return *this;
    }
    self operator--(int) {
        self tmp = *this;
        ++current;
        return tmp;
    }

    // 随机访问操作
    self operator+(difference_type n) const {
        return self(current - n);  // 注意:+n 对应底层 -n
    }
    self& operator+=(difference_type n) {
        current -= n;
        return *this;
    }
    self operator-(difference_type n) const {
        return self(current + n);  // 注意:-n 对应底层 +n
    }
    self& operator-=(difference_type n) {
        current += n;
        return *this;
    }
    reference operator[](difference_type n) const { 
        return *(*this + n); 
    }
};

// 全局比较运算符
template <class Iterator>
inline bool operator==(const reverse_iterator<Iterator>& x,
                       const reverse_iterator<Iterator>& y) {
    return x.base() == y.base();
}

template <class Iterator>
inline bool operator<(const reverse_iterator<Iterator>& x,
                      const reverse_iterator<Iterator>& y) {
    return y.base() < x.base();  // ⚠️ 注意:比较方向也反了!
}

template <class Iterator>
inline typename reverse_iterator<Iterator>::difference_type
operator-(const reverse_iterator<Iterator>& x,
          const reverse_iterator<Iterator>& y) {
    return y.base() - x.base();  // ⚠️ 同样反了!
}

3.3 源码中最"反直觉"的设计

为什么 operator* 访问的是前一个位置?

这是整个反向迭代器设计中最让人困惑的地方。让我用一张图来解释:

正向迭代器视角:
  [1] [2] [3] [4]  [ ]
   ↑               ↑
 begin()         end()
                   ↑
                 (指向最后一个元素之后的位置,
                  即"尾后"位置,不存储有效数据)

反向迭代器包装:
  rbegin() 包装的是 end()
  rend()   包装的是 begin()

当用户调用 *rit (rit 指向 rbegin()):
  - rit 内部的 current 实际上指向 end()(也就是元素 4 后面的位置)
  - 但我们想要的第一个反向元素是 4!
  - 所以需要 *--tmp,即先回退一步再解引用

图解对称性:

         begin()                          end()
            ↓                               ↓
         ┌───┬───┬───┬───┐
         │ 1 │ 2 │ 3 │ 4 │
         └───┴───┴───┴───┘
            ↑               ↑
         rend()          rbegin()
         (包装begin)     (包装end)

rend() 包装 begin() —— 这代表"反向遍历的终点"(不包含 begin 指向的元素)
rbegin() 包装 end() —— 这代表"反向遍历的起点"(实际第一个反向元素是 end()-1)

这样就形成了一个完美对称的结构:

  • begin() ↔ rend()
  • end() ↔ rbegin()
  • 正向区间 [begin(), end()) 对应反向区间 [rbegin(), rend())

这种设计的优雅之处在于——反向遍历的条件和使用方式与正向遍历完全一致:

// 正向遍历
for (auto it = v.begin(); it != v.end(); ++it) { ... }

// 反向遍历 —— 语法完全对称!
for (auto rit = v.rbegin(); rit != v.rend(); ++rit) { ... }

operator++ 和 operator-- 的对称操作

由于"方向相反"这个核心设定,所有的移动操作都要取反:

反向迭代器操作底层正向迭代器操作
++rit--current
--rit++current
rit + ncurrent - n
rit - ncurrent + n
rit1 < rit2rit2.base() < rit1.base()

3.4 手把手实现反向迭代器

理解了 SGI-STL 的源码思路,我们来自己动手实现一个简洁版的反向迭代器。我们将采用模板参数传递数据类型的方式(与 SGI-STL 旧版本类似),相比 iterator_traits 版本更直观,更适合理解核心原理。

ReverseIterator 模板类

// ==================== ReverseIterator.h ====================
// 所有容器的通用反向迭代器
// 迭代器适配器

namespace bit {

template <class Iterator, class Ref, class Ptr>
struct ReverseIterator {
    typedef ReverseIterator<Iterator, Ref, Ptr> Self;

    Iterator _it;  // 被包装的正向迭代器

    // 构造函数
    ReverseIterator(Iterator it) : _it(it) {}

    // ===== 解引用:访问当前位置的前一个元素 =====
    Ref operator*() {
        Iterator tmp = _it;
        return *(--tmp);
    }

    // ===== 箭头操作符 =====
    Ptr operator->() {
        return &(operator*());
    }

    // ===== 前置 ++ → 底层 -- =====
    Self& operator++() {
        --_it;
        return *this;
    }

    // ===== 前置 -- → 底层 ++ =====
    Self& operator--() {
        ++_it;
        return *this;
    }

    // ===== 后置 ++ =====
    Self operator++(int) {
        Self tmp(*this);
        --_it;
        return tmp;
    }

    // ===== 后置 -- =====
    Self operator--(int) {
        Self tmp(*this);
        ++_it;
        return tmp;
    }

    // ===== 比较运算符 =====
    bool operator!=(const Self& s) const {
        return _it != s._it;
    }

    bool operator==(const Self& s) const {
        return _it == s._it;  // 注意课件中的源码有个小笔误,这里是 == 才对
    }
};

} // namespace bit

3.5 将反向迭代器集成到容器中

有了通用的 ReverseIterator 适配器,我们可以轻松地将其集成到任何支持双向迭代器的容器中。

集成到 vector

// ==================== vector.h ====================
#include "ReverseIterator.h"

namespace bit {

template <class T>
class vector {
public:
    typedef T*                                         iterator;
    typedef const T*                                   const_iterator;
    typedef ReverseIterator<iterator, T&, T*>          reverse_iterator;
    typedef ReverseIterator<const_iterator, const T&, const T*>  
        const_reverse_iterator;

    // ===== 正向迭代器 =====
    iterator begin() { return _start; }
    iterator end()   { return _finish; }
    const_iterator begin() const { return _start; }
    const_iterator end()   const { return _finish; }

    // ===== 反向迭代器:包装正向迭代器的对称位置 =====
    reverse_iterator rbegin() { return reverse_iterator(end()); }
    reverse_iterator rend()   { return reverse_iterator(begin()); }
    const_reverse_iterator rbegin() const { 
        return const_reverse_iterator(end()); 
    }
    const_reverse_iterator rend()   const { 
        return const_reverse_iterator(begin()); 
    }

    // ... vector 的其他成员函数 ...

private:
    iterator _start        = nullptr;   // 起始位置
    iterator _finish       = nullptr;   // 结束位置(尾后)
    iterator _endofstorage = nullptr;   // 容量末尾
};

} // namespace bit

集成到 list

// ==================== list.h ====================
#include "ReverseIterator.h"

namespace bit {

template <class T>
class list {
    typedef ListNode<T> Node;
public:
    typedef ListIterator<T, T&, T*>                   iterator;
    typedef ListIterator<T, const T&, const T*>       const_iterator;
    typedef ReverseIterator<iterator, T&, T*>          reverse_iterator;
    typedef ReverseIterator<const_iterator, const T&, const T*>  
        const_reverse_iterator;

    // ===== 正向迭代器(带头双向循环链表) =====
    iterator begin() { return _head->_next; }  // 头结点的下一个
    iterator end()   { return _head; }          // 头结点本身就是"尾后"
    const_iterator begin() const { return _head->_next; }
    const_iterator end()   const { return _head; }

    // ===== 反向迭代器 =====
    reverse_iterator rbegin() { return reverse_iterator(end()); }
    reverse_iterator rend()   { return reverse_iterator(begin()); }
    const_reverse_iterator rbegin() const {
        return const_reverse_iterator(end());
    }
    const_reverse_iterator rend()   const {
        return const_reverse_iterator(begin());
    }

    // ... list 的其他成员函数 ...

private:
    Node*  _head;  // 头结点(哨兵结点)
    size_t _size;
};

} // namespace bit

测试验证

// ==================== test.cpp ====================
#include "list.h"
#include "vector.h"
#include <iostream>
using namespace std;

int main() {
    // ===== 测试 list 的反向迭代器 =====
    bit::list<int> lt = { 1, 2, 3, 4 };
    bit::list<int>::reverse_iterator rit = lt.rbegin();
    while (rit != lt.rend()) {
        cout << *rit << " ";    // 预期输出: 4 3 2 1
        ++rit;
    }
    cout << endl;

    // ===== 测试 vector 的反向迭代器 =====
    bit::vector<int> v = { 1, 2, 3, 4 };
    bit::vector<int>::reverse_iterator vrit = v.rbegin();
    while (vrit != v.rend()) {
        cout << *vrit << " ";   // 预期输出: 4 3 2 1
        ++vrit;
    }
    cout << endl;

    return 0;
}

运行结果:

4 3 2 1
4 3 2 1

完美!🎉 无论是链表还是数组,同一个 ReverseIterator 模板类都能正确适配,这就是模板编程 + 适配器模式的强大之处。


四、总结与思考

本文通过两个硬核案例带领大家深入理解了 C++ 中栈与迭代器适配器的高级应用。让我们做一次回顾和升华:

关于栈

栈不仅仅是一个 push/pop/top 的简单结构。在表达式求值中,栈承担了两个关键角色:

  1. 运算符栈(中缀转后缀阶段):利用 LIFO 特性天然地实现了运算符优先级的延迟处理——优先级低的操作符被压在栈底,等高优先级的先出来算完;
  2. 运算数栈(后缀求值阶段):暂存中间计算结果,"弹出两个、算完压回一个"的节奏恰好匹配后缀表达式的求值顺序。

更深一层想,凡是涉及 “暂存、等待、按序取回” 的场景——括号匹配、函数调用栈、浏览器的前进后退、撤销重做(Undo/Redo)——都有栈的身影。理解栈就是理解"后进先出"这种时间上的嵌套关系。

关于反向迭代器

反向迭代器是适配器设计模式的教科书级案例:

  1. 不修改原有接口——正向迭代器的所有操作保持不变;
  2. 通过包装(Wrap)而非继承来实现功能反转——ReverseIterator 内部持有一个正向迭代器,所有操作都委托给它,只是方向取反;
  3. 对称设计的优雅——rbegin()↔end()、rend()↔begin() 的映射关系,使得反向遍历的使用方式与正向遍历完全一致:for (auto it = rbegin(); it != rend(); ++it);
  4. 模板的泛化能力——一个 ReverseIterator 模板可以适配任何满足双向迭代器要求的容器(vector、list、deque、set、map……),这就是 Generic Programming(泛型编程) 的威力。

从这两个案例看 STL 设计哲学

细心的读者可能已经发现,本文的两个主题有一个共同的底层思想:通过"包装"实现功能的增强和转变。

  • 栈 = 包装 deque/vector/list → 限制接口,提供 LIFO 语义
  • 反向迭代器 = 包装正向迭代器 → 改变遍历方向,保持接口一致

这就是 STL 中无处不在的**适配器(Adapter)**思想——不是重新发明轮子,而是在已有组件基础上,通过巧妙的封装来实现新的抽象。理解这一点,你对整个 STL 的认知会上升到一个新的层次。

【RabbitMQ C++】AMQP-CPP实战:从零构建高效消息队列系统 本文详细介绍了如何使用C++和AMQP-CPP库从零构建高效的RabbitMQ消息队列系统。内容涵盖环境搭建、核心概念解析,并通过实战代码示例演示了如何编写生产者和消费者程序,实现消息的发布与订阅。文章重点讲解了如何与libev事件循环集成,以构建高性能、可靠的异步通信组件,为C++分布式系统开发提供了一套完整的解决方案。 阅读详情

相关推荐

C++:队列和栈

leetcode刷题基础

sinat_40628756的博客 2117

C++数据结构与算法——栈与队列

C++第二阶段——数据结构和算法,之前学过一点点数据结构,当时是基于Python来学习的,现在基于C++查漏补缺,尤其是树的部分。这一部分计划一个月,主要利用来学习,刷题使用力扣网站,不定时更新,欢迎关注!

weixin_63866037的博客 1931

【C++ 005】栈与队列的基础理论

队列是先进先出;queue栈是先进后出。stack1.栈栈是以底层容器完成其所有的⼯作,对外提供统⼀的接⼝,底层容器是可插拔的(也就是说我们可以控制使⽤哪种容器来实现栈的功能)。提供push 和 pop 等等接⼝,所有元素必须符合先进后出规则,所以栈不提供⾛访功能,也不提供迭代器(iterator)。不像是set 或者map 提供迭代器iterator来遍历所有元素。常⽤的SGI STL,如果没有指定底层实现的话,默认是以deque为缺省情况下栈的底层结构。

qq_44917064的博客 727

C++算法学习五.栈与队列

栈与队列的理论基础:栈先进后出,队列先进先出,无迭代器,底层可以是数组链表队列,都叫容器适配器用栈实现队列:模拟一下即可,使用两个栈,一个输出栈,一个输入栈,栈与队列不能空操作,其实就需要想明白弹出操作即可,先把输出栈清空,再把输入栈全部压入,弹出用队列实现栈:也是模拟题,用两个队列实现,一个队列操作,另一个作为辅助队列,其实也是想明白栈的形式原理即可,计算队列的大小,把元素全部放到辅助队列,剩余就是要弹出的元素,在将辅助队列赋值给队列,一个队列也可以实现。

weixin_55605689的博客 2883

C语言及C++实现的栈与队列项目实战

栈(Stack)是一种后进先出(LIFO)的数据结构,类似于一摞盘子,只能从最上面添加或移除元素。而队列(Queue)是一种先进先出(FIFO)的数据结构,类似电影院的排队,元素的添加(入队)发生在队尾,移除(出队)则发生在队首。理解这两种数据结构对于处理复杂的算法和设计高效的程序至关重要。例如,函数调用栈是一个实际中常见的栈应用,它跟踪程序执行过程中的函数调用。队列则常用于模拟真实世界场景,如打印队列或消息处理系统。

weixin_34618526的博客 1010

C# 原生数字孪生的尝试

本文介绍了使用C#实现数字孪生的基本思路。首先从爱普生官网下载机器人3D模型,然后通过Blender创建并导出带动画的FBX模型。在WPF项目中,使用HelixToolkit.SharpDX等插件加载模型,通过Viewport3DX进行3D渲染。关键点是通过动画控制器接收真实设备信号来驱动模型动画,实现物理实体与虚拟模型的同步。文中提供了完整的代码实现,包括模型导入、动画播放控制等核心功能。该方法为构建数字孪生系统提供了一种可行的技术方案。

qq_33529095的博客 1329

C++栈和队列的用法

前言 在C++标准库中,用C++中的类实现了栈和队列,也实现了他们的常用的一些操作。 一、栈 1.用法详细 栈被包含在头文件#include<stack>中。 栈的定义以及常用操作: stack<int> st; //定义整型栈,也可以定义其他类型的 stack<T> s; //自定义类型的栈,T可以是C++的基本数据类型也可以是自定义的数据类型 st.push(e); //把e塞入栈中,e的类型必须和定义时的一样 st.pop(); //把栈顶

将~军的博客 2349

测试丢包_电信IPRAN承载LTE业务丢包故障处理案例

银川 景旭阳电信IPRAN承载LTE业务丢包故障处理案例一.故障现象某地市IPRAN网络承载LTE基站业务,客户反馈近期丢包严重,基站侧ping侧核心网EPC丢包达到10%左右;且基站数量在10个左右,并分布在同一地区,需要IPRAN网络协助排查;随后查看了IPRAN网络;发现这些业务所在基站的PW-BFD历史告警存在频繁的UP/DOWN告警;综上信息展开故障处理,初步判断由于BFD频繁...

weixin_29740921的博客 1909

【LeetCode】栈和队列进阶题目

C++栈和队列基础题:最小栈、栈的压入弹出序列、逆波兰表达式求值。含算法代码优化!

有梦想的电信狗 1071

COT Buck和普通Buck的区别

传统的电流模控制的调制原理,由时钟脉冲信号 clk 的上升沿触发开始一个新的开关周期,d 由低变高,主开关闭合,流过它的电流和电感电流相等,并以固定斜率逐渐增大,直至 iL *Ri 与斜坡补偿信号 se 的和等于控制信号 vc;此时,比较器的输出电平由低变高,导通时间产生器被触发,其输出 d 由低变高,并产生一个固定的导通时间 Ton;然后 PWM 比较器的输出由低变高,导通时间产生器被触发,主开关闭合,电感电流逐渐增大,直到导通时间产生器计满固定的时间 Ton,d 变为低电平,主开关断开,周期结束。

模拟芯片设计 1125

深入了解BPM及BPM业务流程管理系统

BPM 即业务流程管理,是一种以规范化的构造端到端的卓越业务流程为中心,以持续地提高组织业务绩效为目的的系统化方法。它涵盖了业务流程的整个生命周期,包括流程的设计、执行、监控、分析和优化。BPM 作为一种先进的管理方法,对于企业提高运营效率、降低成本、提升客户满意度和增强竞争力具有重要意义。BPM 流程管理系统为企业实现 BPM 提供了强大的工具和支持,而 F2BPM 流程管理平台以其先进的技术架构、丰富的功能模块、简单易用的操作界面和强大的定制能力,成为企业实现业务流程管理的理想选择。

Stone3625的博客 2950

栈和队列(用C++实现栈和队列)

栈是一种后进先出的线型结构,C++实现栈的代码如下: #include using namespace std; #define MAXLEN 50 typedef struct { char key[10]; char name[20]; int age; }Data; typedef struct { Data nodeData[MAXLEN+1]; int top; }St

赞美月亮的专栏 4362

bevformer详解(3): 逐行代码讲解

在这篇文章中,我们将从BEVFormer算法代码入手,深入探究BEV转换的流程。BEVFormer是一种基于Transformer的BEV转换模型,其核心思想是利用自注意力机制和交叉注意力机制,从,并通过Object Query与BEV特征的交互,完成3D目标检测和地图分割等任务。https://developer.baidu.com/article/details/3224715bevformer的网络结构由三部分组成, 模型结构所示,其中的是部分,对于的是的结构,对于部分,沿用的中的decoder部分,

@bangbang的博客 3697

【C++】STL:栈和队列模拟实现

C++中的stack(栈)和queue(队列)是两种常见的数据结构,用于存储和管理数据。栈是一种先进后出(LIFO)的数据结构,类似于我们平时堆叠的一摞书,只能在顶部进行操作。在C++中,可以使用std::stack模板类来创建栈。栈的主要操作包括压入(push)元素到栈顶、弹出(pop)栈顶元素以及获取栈顶元素等。队列是一种先进先出(FIFO)的数据结构,类似于排队等候的人群,新元素插入队尾,最早插入的元素在队头。在C++中,可以使用std::queue模板类来创建队列。

Renswc的博客 2750

【图像拼接】论文精读:As-Rigid-As-Possible Shape Manipulation(ARAP)

文章目录前言1.摘要2.算法的总体流程总结 前言 图像变形算法 1.摘要 论文提出了一个二维图形互动系统。图形是建立在三角形网格上的,用户只需移动几个网格顶点(限制顶点),系统通过最小化每个三角形的误差计算网格剩余顶点(自由顶点)的位置。 算法分两步: 适当旋转每个三角形。 调整大小。 关键是使用二次度量误差将最小化问题转换成线性方程组,然后再解这个方程组。 刚性形状变形是很难的,论文的研究使其变得简单。 2.算法的总体流程 总的流程分三步: 将图形用三角形网格覆盖 选择三个点(限制顶点) 操纵限

主要更新底层视觉(去噪、超分等)相关的科研内容,形式为【论文精读】+【论文复现】 15万+

【深度学习】脸部修复,CodeFormer,论文,实战

Blind face restoration是一个高度 ill-posed 的问题,通常需要辅助引导来 1) 改善从降级输入到期望输出的映射,或者 2) 补充在输入中丢失的高质量细节。在本文中,我们展示了在一个小的代理空间中学到的离散码本先验可以通过将盲目的面部恢复建模为 a code prediction task来大大减少恢复映射的不确定性和模糊性,同时为生成高质量面部提供丰富的视觉元素。

q742971636的博客 2989

【CPP】栈、双端队列、队列、优先级队列与反向迭代器

栈、双端队列、队列、优先级队列与反向迭代器简单介绍与模拟实现,由了解到认识~

BinaryWiker的博客 1215

数据结构课程设计--基于知识图谱的中医食疗数据管理与应用

【代码】数据结构课程设计--基于知识图谱的中医食疗数据管理与应用。

M_inherit的博客 565

C++ 栈和队列的介绍与使用

使用标准库的栈和队列时,先包含相关的头文件 #include #include 定义栈如下: stack stk; 定义队列如下: queue q; 栈提供了如下的操作 s.empty() 如果栈为空返回true,否则返回false s.size() 返回栈中元素的个数 s.pop() 删除栈

Tham 在思索中前行! 2万+

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

栈与队列及其C++实现

栈与队列总结及其C++实现栈及队列简介栈队列栈及队列的实现栈的实现队列的实现 栈及队列简介 栈 栈是一种后进先出的数据结构即LIFO。 通常,插入操作在栈中被称作入栈,在栈的末尾添加一个新元素。删除操作被称作出栈,删除操作也发生在栈的末尾。即对于栈来说插入和删除操作均发生在栈的同一端。 如下图,在LIFO数据结构中,将首先处理添加到队列中的最新元素。 在C++ STL标准库中 <stack...

NEUChords的博客 1489

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

C++ stack&&queue 栈和队列的使用&&模拟实现

讲解C++的栈和队列的使用与模拟实现。模拟实现中,用文字叙述来帮助读者更好地理解堆和栈的模拟实现。

weixin_57761086的博客 2552

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

C++ 栈(Stack)与队列(Queue)深度解析:从原理到实战

定义:将一个类的接口转换为另一种接口,使原本不兼容的类可以协同工作。STL 中的应用stack和queue本质是容器适配器,通过封装其他容器(如dequevector)的接口实现特定功能。数据结构访问规则底层容器(STL 默认)典型场景stack后进先出(LIFO)deque函数调用、表达式求值queue先进先出(FIFO)dequeBFS、任务队列优先级优先vector(堆结构)堆排序、最短路径算法。

2403_87794049的博客 1121

【C++】栈和队列的模拟实现 & 经典题目解析

【C++】栈和队列模拟实现和经典习题悉数剖析,清晰思路结合图文带你搞定练习题,真的不点进来看看吗?

qq_42996461的博客 1223

C++用两个队列实现栈

1. 基础 队列:先进先出,即插入数据在队尾进行,删除数据在队头进行; 栈:后进先出,即插入与删除数据均在栈顶进行。 2. 思路 两个队列实现一个栈的思想:用dataQueue队列作为push数据的队列,用helpQueue队列暂存数据。 只要是对栈进行push操作,就将数据push入dataQueue队列中。 要实现栈的pop操作,就要在dataQueue队列不为空的情况下,把...

暮日落流年的博客 3308

队列实现栈与栈实现队列

加深栈和队列的理解

2302_79539362的博客 1330

20200927:Java和Cpp中栈与队列的区别

Java和Cpp中栈与队列的区别题目思路与算法代码实现对比分析两种语言中栈和队列的区别 题目 1.225. 用队列实现栈 2.232. 用栈实现队列 3.155. 最小栈 思路与算法 三道简单力扣习题之前用Java提交过题解,这次用Cpp来进行提交,从而加深二者的stl或者是对应的util工具类中的方法的使用。 代码实现 1.225. 用队列实现栈 class MyStack { public: /** Initialize your data structure here. */ My

Mr IMMUNIZE的博客 195
上一篇: C++智能指针的使用及其原理
下一篇: C++继承与多态进阶
jinyishu_
博客等级 码龄1年 53粉丝 82原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值