CPP的Stack模拟实现
Stack的定义
Stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作。
stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(即栈顶)被压入和弹出。
stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器类应该支持以下操作:
- empty:判断是否为空
- back:获取尾部元素
- push_back:尾部插入元素
- pop_back:尾部删除元素
标准容器vector, queue,list都符合构建stack的要求, 但是默认情况下, 没有为stack指定底层容器, 默认为deque.
Stack使用
要模拟实现Stack, 我们首先要会用Stack, 以下是Stack的几个成员函数的CPlusPlus手册链接.
函数方法 CPlusPlus链接 empty() empty链接 size() size链接 top() top链接 push() push链接 emplace() emplace链接 pop() pop链接 这里用一道经典的OJ题来熟悉Stack的使用, 155. 最小栈 - 力扣(Leetcode)
题解如下:
class MinStack { public: MinStack() { } void push(int val) { if(minStack.empty() || minStack.top() >= val) { minStack.push(val); } data.push(val); } void pop() { if(!minStack.empty()) { if(minStack.top() == data.top()) { minStack.pop(); } data.pop(); } else { data.pop(); } } int top() { return data.top(); } int getMin() { return minStack.top(); } // 6 7 8 9 1 2 3 4 // 6 private: stack<int> data; stack<int> minStack; }; /** * Your MinStack object will be instantiated and called as such: * MinStack* obj = new MinStack(); * obj->push(val); * obj->pop(); * int param_3 = obj->top(); * int param_4 = obj->getMin(); */
Stack的模拟实现
在熟悉了Stack如何实现之后, 我们开始来实现Stack, 由于Stack底层可以选的数据结构比较多, 这里选择比较简单的vector(虽然说从实际使用上, 我们并不需要关心vector和list和deque的底层区别).
实现的代码如下:
#include <iostream> #include <vector> namespace pc { template <class T> class stack { public: stack() { } void push(const T& data) { data_.push_back(data); } void pop() { if (!data_.empty()) { data_.pop_back(); } } T& top() { return data_.back(); } const T& top() const { return data_.back(); } size_t size() const { return data_.size(); } bool empty() const { return data_.empty(); } void display() const { for (const T& data : data_) { std::cout << data << " "; } std::cout << std::endl; } private: std::vector<T> data_; }; }
注意:
如果栈为空去top是会报错的, 这并不是我没有去处理, 而是标准模板库的stack就没有处理这个, 从理论上讲, 返回top的结果这种方法处理不了这种问题.
以下为std的报错信息, 可以看到是deque的报错, 说明两个问题:
- std在设计时可能考虑到接口的普遍性, 即top直接返回栈顶, 没有去修改函数样式, 导致这个问题没法解决.
- std的默认底层用的是deque.
假设我们现在可以去修改函数的样式, 我们可不可以解决这个问题呢? 当然可以, 我现在有一个解决方法, 就是用指针进行传参, 函数返回读取是否成功. 函数具体如下:
bool top(T* data) { if (!data_.empty()) { *data = data_.back(); return true; } return false; }