栈和队列
一.stack的介绍和使用
(1)概念
- stack是一种后进先出的容器结构,准确来说stack并不是容器,而是一种容器适配器(对特定类封装作为其底层的容器)。
(2)常见接口
函数说明 | 接口说明 |
---|---|
stack() | 构造空的栈 |
empty() | 判断栈是否为空 |
size() | 返回stack中的元素个数 |
top() | 返回栈顶元素的引用 |
push() | 将元素压入stack中 |
pop() | 将stack栈顶的元素弹出 |
(3)使用
1.最小栈问题
最小栈
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
思路:使用两个栈,一个正常存放元素,另一个存放最小值,每次入栈时比较更新即可。
class MinStack {
public:
MinStack() {
}
void push(int val) {
if(_minst.empty() || val <= _minst.top())//_minst为空或者val值小于_minst中最小值则入栈
{
_minst.push(val);
}
_st.push(val);
}
void pop() {
if(_minst.top() == _st.top())//若_st出栈的刚好为最小值,则_minst亦出栈
{
_minst.pop();
}
_st.pop();
}
int top() {
return _st.top();
}
int getMin() {
return _minst.top();
}
private:
stack<int> _st;
stack<int> _minst;
};
2.栈的压入、弹出序列
栈的压入、弹出序列
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。
思路:用一个栈模拟栈的压入、弹出即可,每次将压入顺序数组的元素入栈,然后若栈顶元素与弹出顺序的数组元素相同,那么直到栈顶与之不同之前,都将栈中元素出栈。
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV) {
stack<int> st;
int pushi = 0, popi = 0;
while(pushi < pushV.size())//当pushi未到尽头时就将pushi入栈
{
st.push(pushV[pushi++]);
while(!st.empty() && st.top() == popV[popi])//若st栈顶元素与popi处相同则出栈
{
popi++;
st.pop();
}
}
//若此时st为空,则入出栈对应
return st.empty();
}
};
(4)stack 的模拟实现
//Container时容器适配器,默认是deque,即双端队列
template <class T, class Container = deque<T>>
class stack
{
private:
Container _c;
};
#pragma once
namespace lyl
{
template <class T, class Container = deque<T>>
class stack
{
public:
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()
{
return _c.size();
}
bool empty()
{
return _c.empty();
}
private:
Container _c;
};
void test_stack()
{
stack<int> st;
st.push(1);
st.push(2);
st.push(3);
st.push(4);
st.push(5);
cout << st.size() << endl;
while (!st.empty())
{
cout << st.top() << " ";
st.pop();
}
cout << endl;
cout << st.empty() << endl;
}
}
二.queue的介绍和使用
(1)概念
- 队列是一种后进后出的容器适配器结构,标准容器类deque和list满足其要求。默认情况下,如果没有为queue实例化指定容器类,则使用标准容器deque(双端队列)。
(2)常见接口
函数说明 | 接口说明 |
---|---|
queue() | 构造空的队列 |
empty() | 判断队列是否为空 |
size() | 返回队列中元素的个数 |
front() | 返回队头元素 |
back() | 返回对尾元素 |
pop() | 将队头元素出队 |
(3)使用
用队列使用栈
用队列实现栈
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
思路:保证有一个栈始终为空,另一个接受元素。若元素入队,则入非空栈中;若出队头元素,则将一个栈中的元素倒到另一个栈,直到只剩一个元素再出栈即可:
class MyStack {
public:
MyStack() {
}
void push(int x) {
if(!_q1.empty())
{
_q1.push(x);
}
else
{
_q2.push(x);
}
}
int pop() {
if(!_q1.empty())
{
while(_q1.size() != 1)
{
_q2.push(_q1.front());
_q1.pop();
}
int top = _q1.front();
_q1.pop();
return top;
}
else
{
while(_q2.size() != 1)
{
_q1.push(_q2.front());
_q2.pop();
}
int top = _q2.front();
_q2.pop();
return top;
}
}
int top() {
if(!_q1.empty())
{
while(_q1.size() != 1)
{
_q2.push(_q1.front());
_q1.pop();
}
int top = _q1.front();
_q1.pop();
_q2.push(top);
return top;
}
else
{
while(_q2.size() != 1)
{
_q1.push(_q2.front());
_q2.pop();
}
int top = _q2.front();
_q2.pop();
_q1.push(top);
return top;
}
}
bool empty() {
return _q1.empty() && _q2.empty();
}
private:
queue<int> _q1;
queue<int> _q2;
};
(4)queue的模拟实现
template <class T, class Container = deque<T>>
class queue
{
private:
Container _c;
};
#pragma once
namespace lyl
{
template <class T, class Container = deque<T>>
class queue
{
public:
void push(const T& x)
{
_c.push_back(x);
}
void pop()
{
_c.pop_front();
}
T& back()
{
return _c.back();
}
const T& back() const
{
return _c.back();
}
T& front()
{
return _c.front();
}
const T& front() const
{
return _c.front();
}
size_t size()
{
return _c.size();
}
bool empty()
{
return _c.empty();
}
private:
Container _c;
};
void test_queue()
{
queue<int> q;
q.push(1);
q.push(2);
q.push(3);
q.push(4);
q.push(5);
cout << q.size() << endl;
while (!q.empty())
{
cout << q.front() << " ";
q.pop();
}
cout << endl;
cout << q.empty() << endl;
}
}
三、priority_queue的介绍和使用
(1)概念
- 优先队列是一种容器适配器,根据严格的弱排序标准(默认情况),它的第一个元素总是它所包含的元素中最大的。
- 简单来说,priority_queue类似于数据结构中的堆,其使用与实现也和堆(Heap)并无二异。
(2)常见接口
函数说明 | 接口说明 |
---|---|
priority_queue() | 构造一个空的优先级队列 |
empty() | 判断优先级队列是否为空 |
top() | 返回优先级队列中最大(最小)元素,即堆顶元素 |
push() | 在优先级队列中插入元素 |
pop() | 删除优先级队列中最大(最小)元素,即堆顶元素 |
(3)使用
TopK问题
数组中的第K个最大元素
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
思路:使用一个k个数降序的优先级队列(小堆),先将数组中前k个元素入堆,然后遍历数组中剩下的元素,若其大于堆顶元素(堆中最大的元素),则将其入堆,这样最终堆顶元素就是数组中的第k和最大元素,整个堆就是数组中最大的k个数。
这样理解:若数组元素比堆顶元素(最大的元素)还要大,那么将其进堆,最终的小堆就是数组中最大的k个元素。
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
if(nums.size() == 1)
{
return nums[0];
}
//建k个数的小堆
priority_queue<int, vector<int>, greater<int>> pq;
//将数组中的前k个数进堆
for(int i = 0; i < k; i++)
{
pq.push(nums[i]);
}
//将数组中剩下的数与堆顶元素比较,若大于堆顶,则弹出堆顶元素,并将数组的数入堆
for(int i = k; i < nums.size(); i++)
{
if(pq.top() < nums[i])
{
pq.pop();
pq.push(nums[i]);
}
}
//最后,堆顶元素即为第k个最大的元素
return pq.top();
}
};
(4)priority_queue的模拟实现
仿函数的简单介绍
由于优先级队列的默认顺序是升序,而我们如果要让priority_queue的顺序改为降序(大堆),对于C语言就是用一个函数指针,而对于C++则可以使用仿函数。
C++中,通过在一个类中重载括号运算符的方法使用一个函数对象而不是一个普通函数来实现仿函数。
比如:
//构造一个小堆,其中,greater就是仿函数作为参数
priority_queue<int,vector<int>,greater<int>> q(v.begin(), v.end());
代码实现
template <class T, class Container = vector<T>, class Compare = less<T>>
class priority_queue
{
private:
Container _con;
};
#pragma once
namespace lyl
{
template<class T>
class Greater
{
public:
bool operator()(const T& x, const T& y)
{
return x > y;
}
};
template<class T>
class Less
{
public:
bool operator()(const T& x, const T& y)
{
return x < y;
}
};
template <class T, class Container = vector<T>, class Compare = Less<T>>
class priority_queue
{
void AdjustUp(int child)
{
int parent = (child - 1) / 2;
while (child > 0)
{
//if (_con[parent] < _con[child])
if (com(_con[parent], _con[child]))
{
swap(_con[child], _con[parent]);
//迭代
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
void AdjustDown(int parent)
{
size_t child = parent * 2 + 1;
while (child < size())
{
//选出左右孩子中值较大的那个
//if (child + 1 < size() && _con[child] < _con[child + 1])
if (child + 1 < size() && com(_con[child], _con[child + 1]))
{
child++;
}
//if (_con[parent] < _con[child])
if (com(_con[parent], _con[child]))
{
swap(_con[parent], _con[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
public:
priority_queue()
{}
template <class InputIterator>
priority_queue(InputIterator first, InputIterator last)
{
while (first != last)
{
push(*first);
++first;
}
//while (first != last)
//{
// _con.push_back(*first);
// ++first;
//}
建堆
//for (int i = (size() - 1 - 1) / 2; i >= 0; i--)
//{
// AdjustDown(i);
//}
}
void push(const T& x)
{
_con.push_back(x);
AdjustUp(size() - 1);
}
void pop()
{
swap(_con[0], _con[size() - 1]);
_con.pop_back();
AdjustDown(0);
}
const T& top() const
{
return _con[0];
}
size_t size()
{
return _con.size();
}
bool empty()
{
return _con.empty();
}
private:
Container _con;
Compare com;
};
void test_priority_queue()
{
priority_queue<int> pq;
pq.push(1);
pq.push(2);
pq.push(3);
pq.push(4);
while (!pq.empty())
{
cout << pq.top() << " ";
pq.pop();
}
cout << endl;
vector<int> v = { 1,3,5,7,9,2,4,6,8,10 };
priority_queue<int,vector<int>,Greater<int>> q(v.begin(), v.end());
while (!q.empty())
{
cout << q.top() << " ";
q.pop();
}
cout << endl;
}
}