文章目录
queue的介绍、使用以及实现
queue的介绍
- 队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元素,另一端提取元素。
- 队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从队尾入队列,从队头出队列。
- 底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操作:
- empty:检测队列是否为空
- size:返回队列中有效元素的个数
- front:返回队头元素的引用
- back:返回队尾元素的引用
- push_back:在队列尾部入队列
- pop_front:在队列头部出队列
queue的使用
函数接口声明以及对应说明:
queue() 构造空的队列
empty() 检测队列是否为空,是返回true,否则返回false
size() 返回队列中有效元素的个数
front() 返回队头元素的引用
back() 返回队尾元素的引用
push() 在队尾将元素val入队列
pop() 将队头元素出队列
void test()
{
queue<int> q;
q.push(1);
q.push(2);
q.push(3);
while (!q.empty())
{
cout << q.front() << " ";
q.pop();
}
cout << endl;
}
用一个队列实现栈的结构
class MyStack
{
public:
void push(int x)
{
q.push(x);
}
int pop()
{
int top;
int sz = q.size();
while (sz > 1)
{
int front = q.front();
q.pop();
q.push(front);
--sz;
}
top = q.front();
q.pop();
return top;
}
int top()
{
return q.back();
}
bool empty()
{
return q.empty();
}
private:
queue<int> q;
};
queue的实现
1.顺序表:
push(入队):push_back 尾插
pop(出队): pop_front 头删(O(n)操作)
front:front
2.链表(含有尾指针的单链表):
push(入队):push_back 尾插
pop(出队): pop_front 头删(O(n)操作)
front:front
//用list实现
template <class T>
class Queue
{
public:
void push(const T& val)
{
_q.push_back(val);
//_q.push_front(val);
}
void pop()
{
_q.pop_front();
//_q.pop_back();
}
T& front()
{
return _q.front();
//return _q.back();
}
size_t size() const
{
return _q.size();
}
bool empty() const
{
return _q.empty();
}
private:
list<T> _q;
};
priority_queue的介绍、使用以及实现
priority_queue的介绍
- 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
- 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元
素)。 - 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特
定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。 - 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭
代器访问,并支持以下操作:
- empty():检测容器是否为空
- size():返回容器中有效元素个数
- front():返回容器中第一个元素的引用
- push_back():在容器尾部插入元素
priority_queue的使用
函数接口声明以及说明:
priority_queue() / priority_queue(first,last):构造一个空的优先级队列
empty( ):检测优先级队列是否为空,是返回true,否则返回false
top( ) :返回优先级队列中最大(最小元素),即堆顶元素
push(x) :在优先级队列中插入元素x
pop(): 删除优先级队列中最大(最小)元素,即堆顶元素
void test()
{
//默认是大堆结构
priority_queue<int> q;
q.push(100);
q.push(12);
q.push(145);
q.push(15);
while (!q.empty())
{
cout << q.top() << " ";
q.pop();
}
cout << endl;
//最终输出为一个由大到小的有序输出
}
当要传入自定义类型时,必须要对自定义类型的比较运算符进行重载,让优先级队列知道怎么比较大小。
struct A
{
A(int a)
:_a(a)
{ }
bool operator<(const A& a) const
{
return _a < a._a;
}
int _a = 1;
};
void test()
{
//当要传入自定义类型时,必须要对自定义类型的比较运算符进行重载,让优先级队列知道怎么比较大小
priority_queue<A> q;
q.push(A(10));
q.push(A(3));
q.push(A(155));
q.push(A(32));
while (!q.empty())
{
cout << q.top()._a << " ";
q.pop();
}
cout << endl;
}
priority_queue的实现
因为要实现堆的向上调整和向下调整,因此使用顺序表
底层时堆的操作:
push:尾插,向上调整
pop:堆顶元素和最后的叶子节点交换后再进行尾删向下调整:arr[0] <—> arr[size-1]交换,尾删,向下调整
top:arr[0]
template <class T>
class Priority_Queue
{
public:
//大根堆
shiftUp(int child)
{
//定义父亲节点索引
int parent = (child - 1) / 2;
//当插入的最后的叶子节点更新到堆顶时结束
while (child > 0)
{
//将孩子节点和父亲节点比较,大于时交换,更新索引
if (v[child] > v[parent])
{
swap(v[child], v[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
break;
}
}
shiftDown()
{
//默认从堆顶元素向下调整
int parent = 0;
//定义左孩子索引
child = parent * 2 + 1;
while (child < v.size())
{
//如果右孩子存在并且右孩子大于左孩子,则将child更新
if (child + 1 < v.size() && v[child] < v[child + 1])
++child;
//并交换孩子和父亲节点的值,再更新索引
if ([v[parent] < v[child])
{
swap(v[child], v[parent]);
parent = child;
child = parent * 2 + 1;
}
else
break;
}
}
void push(const T& val)
{
//尾插后,向上调整,使得满足优先队列大根堆条件
v.push_back(val);
shiftUp(v.size() - 1);
}
void pop()
{
//要删除堆顶元素,先将堆顶元素和最后叶子节点交换后删除,再向下调整
swap(v[0], v[v.size() - 1]);
v.pop_back();
shiftDown();
}
void top()
{
return v[0];
}
size_t size() const
{
return v.size();
}
bool empty() const
{
return v.empty();
}
private:
vector<T> v;
};