个人主页~
Stack
- 一、Stack的介绍和使用
- 1、stack的介绍
- 2、stack的使用
- 3、stack的模拟实现
- 二、容器适配器
- 1、什么是适配器
- 2、容器适配器的使用
- 三、deque
- 1、原理介绍
- 2、deque的使用
- 3、deque的缺陷
一、Stack的介绍和使用
1、stack的介绍
stack详细解释
stack是一种容器适配器,专门用来处理后进先出操作,其删除只能从容器的一端进行元素的插入和提取操作
stack是作为容器适配器被实现的,容器适配器是对特定类封装为其底层的容器,并提供一组特定的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部被压入和弹出
stack的底层容器可以是任何标准的容器类模版或者一些其他特定的容器类,这些容器都要支持empty判空、back获取尾部元素、push_back尾部插入元素、pop_back尾部删除元素的操作,这些是stack的基本接口
标准容器vector、list、deque均符合这些要求,如果没有指定stack的底层容器,默认为deque,这其中只有deque没有学习过,后面拿一个段落专门解释deque
2、stack的使用
函数说明 | 接口说明 |
---|---|
stack | 构造空的栈 |
empty | 检测stack是否为空 |
size | 返回stack中的元素个数 |
top | 返回栈顶元素的引用 |
push | 将元素val压入stack中 |
pop | 将stack中尾部的元素弹出 |
void test_stack()
{
stack<int> st;
st.push(1);
st.push(2);
st.push(3);
st.push(4);
while (!st.empty())
{
cout << st.top() << endl;
st.pop();
}
}
手感火热做道题
最小栈
解题思路:就是创建两个栈st和minst,st栈存放数据,minst栈存放最小值,第一个入st栈的直接入minst栈,每次入st栈都与minst栈的top比较一下是不是更小,如果数小就入minst栈
class MinStack
{
public:
MinStack()
{}
//空构造构造空栈
void push(int val)
{
if(minst.empty() || val <= minst.top())
{
minst.push(val);
}
st.push(val);
}
//如果minst栈为空或者入栈的值小于等于minst的栈顶元素就入minst栈
void pop()
{
if(st.top() == minst.top())
{
minst.pop();
}
st.pop();
}
//如果删除st栈顶元素与minst栈顶的元素相等就连minst栈顶元素一块删除
int top()
{
return st.top();
}
int getMin()
{
return minst.top();
}
//因为minst是被比较出来的,越往上的元素越小,所以栈顶元素就是最小的元素
stack<int> st;
stack<int> minst;
};
栈的压入、弹出序列
这个题就是写一个栈弹出顺序是否正确的函数,传给两个vector,然后pushV元素压入栈,然后取栈顶元素与popV的第一个元素进行对比,如果相同就出栈,如果不同就继续入栈,最后如果pushV遍历完后栈为空,那么就正确,否则就错误
bool IsPopOrder(vector<int>& pushV, vector<int>& popV)
{
stack<int> st;
size_t pushi = 0,popi = 0;//用于记录pushV、popV的下标
while(pushi < pushV.size())//如果下标小于size循环继续
{
st.push(pushV[pushi++]);//pushV元素压入栈
while(!st.empty() && st.top() == popV[popi])
{
st.pop();
popi++;
}//然后取栈顶元素与popV的第一个元素进行对比,如果相同就出栈
}
return st.empty();
}
3、stack的模拟实现
stack.h
#pragma once
#include <vector>
#include <deque>
#include <list>
#include <iostream>
namespace little_monster
{
template<class T,class Container = std::deque<T>>
class stack
{
public:
stack()
{}
void push(const T& x)
{
_c.push_back(x);
}
void pop()
{
_c.pop_back();
}
T& top()
{
return _c.back();
}
const T& top() const
{
return _c.back();
}
size_t size() const
{
return _c.size();
}
bool empty() const
{
return _c.empty();
}
private:
Container _c;
};
}
stack可以通过vector为底层实现,也可以通过list为底层实现,直接调用这些模版的接口就可以,不用在从零开始定义成员变量了
这里的Container以及deque是什么呢
二、容器适配器
1、什么是适配器
适配器是一种设计模式,该种设计模式是将一个类的接口转换成用户希望的另外一个接口,适配器可以接受不同的容器来达到用户想要的效果,而stack和queue的默认适配器是deque,priority_queue的默认适配器是vector
2、容器适配器的使用
常见于stack、queue、priority_queue、优先队列中
三、deque
相关文档
1、原理介绍
deque是一种双开口的连续空间的数据结构,可以在头尾两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素,与list比较,空间利用率比较高
deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组
双端队列底层是一段假想的连续空间,实际是分段连续的,为了维护其“整体连续”以及随机访问的假象,落在了deque的迭代器身上
偷一张详解图
首先我们可以看到start迭代器,它由四个指针组成,cur表示当前位置,first表示第一个位置,last表示最后一个位置,node是指向map的其中一个变量
当cur等于last时,node指向下一个位置,然后cur、first、last都重置到下一个map元素,在图中就是cur、first指向8,last指向15后一个位置
当start迭代器中的node和finish迭代器中的node相同时,该数组就是最后一个数组
然后这个map其实是一个指针数组,它进行扩容时是从中间向两端地存储,如果我们进行头插,start指向的数组又满了,我们就在start前面的位置再开一个数组,并且把数据存储在这个数组的最后一个位置
2、deque的使用
void test_deque()
{
std::deque<int> dq;
dq.push_back(3);
dq.push_back(4);
dq.push_front(2);
dq.push_front(1);
for (size_t sz = 0; sz < dq.size(); sz++)
{
std::cout << dq[sz] << " ";
}
std::cout << std::endl;
}
3、deque的缺陷
它不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某小段小空间的边界,导致效率低下,因此在实际中,需要线性结构时,大多数优先使用vector和list,但我们知道的一个应用就是STL中做stack和queue的底层数据结构
它结合了vector和list的部分优点,也得到了它们的部分缺点,与vector相比,deque的优势是头插头删和扩容时,不需要搬移元素,效率高,与list比较,其底层是连续的空间,空间利用率较高
今日分享就到这里~