文章目录
- 0.码云完整代码
- 1.deque的认识
- 1.1介绍
- 1.2图析
- 1.3性能比较
- 2.stack的学习
- 2.1模拟实现
- 2.2测试函数
- 3.queue的学习
- 3.1模拟实现
- 3.2测试函数
- 4.优先级队列的学习
- 4.0仿函数的引入
- 4.1介绍
- 4.2例题
- 4.3模拟实现
- 5.测试函数
0.码云完整代码
点击 栈 队列 优先级队列 跳转码云获取完整代码
1.deque的认识
1.1介绍
双端队列
Deque(通常读作“deck”)是double-ended queue的不规则首字母缩写。双端队列是动态长度的序列容器,可以在两端(前端或后端)伸缩。
特定的库可能以不同的方式实现deque,通常是某种形式的动态数组。但在任何情况下,它们都允许通过随机访问迭代器直接访问各个元素,存储空间通过根据需要扩展和收缩容器来自动处理。
因此,它们提供了类似于向量的功能,但可以高效地在序列的开头和末尾插入和删除元素。但是,与向量不同的是,deque不能保证将其所有元素存储在连续的存储位置上:通过偏移指向另一个元素的指针来访问deque中的元素会导致未定义的行为。
vector和deque提供了非常相似的接口,也可以用于类似的目的,但两者的内部工作方式却完全不同:vector使用单一数组,需要偶尔重新分配空间以适应增长,而deque中的元素可能分散在不同的存储块中,容器在内部保存了必要的信息,以便以常量时间和统一的顺序接口(通过迭代器)直接访问其中的任何元素。因此,deque的内部结构比vector稍微复杂一些,但这使得它们在某些情况下可以更高效地增长,特别是在序列很长时,重分配的开销会变得更大。
对于频繁地在非起始或结束位置插入或删除元素的操作,deque的性能更差,迭代器和引用的一致性也不如列表和前向列表。
Double ended queue
deque (usually pronounced like "deck") is an irregular acronym of double-ended queue. Double-ended queues are sequence containers with dynamic sizes that can be expanded or contracted on both ends (either its front or its back).
Specific libraries may implement deques in different ways, generally as some form of dynamic array. But in any case, they allow for the individual elements to be accessed directly through random access iterators, with storage handled automatically by expanding and contracting the container as needed.
Therefore, they provide a functionality similar to vectors, but with efficient insertion and deletion of elements also at the beginning of the sequence, and not only at its end. But, unlike vectors, deques are not guaranteed to store all its elements in contiguous storage locations: accessing elements in a deque by offsetting a pointer to another element causes undefined behavior.
Both vectors and deques provide a very similar interface and can be used for similar purposes, but internally both work in quite different ways: While vectors use a single array that needs to be occasionally reallocated for growth, the elements of a deque can be scattered in different chunks of storage, with the container keeping the necessary information internally to provide direct access to any of its elements in constant time and with a uniform sequential interface (through iterators). Therefore, deques are a little more complex internally than vectors, but this allows them to grow more efficiently under certain circumstances, especially with very long sequences, where reallocations become more expensive.
For operations that involve frequent insertion or removals of elements at positions other than the beginning or the end, deques perform worse and have less consistent iterators and references than lists and forward lists.
1.2图析
1.3性能比较
//性能测试vector比deque快大约2倍
void test_op()
{
srand(time(0));
const int N = 100000;
vector<int> v;
v.reserve(N);
deque<int> dp;
for (int i = 0; i < N; ++i)
{
auto e = rand();
v.push_back(e);
dp.push_back(e);
}
int begin1 = clock();
sort(v.begin(), v.end());
int end1 = clock();
int begin2 = clock();
sort(dp.begin(), dp.end());
int end2 = clock();
printf("vector sort:%d\n", end1 - begin1);
printf("deque sort:%d\n", end2 - begin2);
}
int main()
{
test_op();
return 0;
}
2.stack的学习
2.1模拟实现
#pragma once
#include <iostream>
#include <assert.h>
#include <deque>
using namespace std;
namespace apex
{ // Double-ended queue 双端队列
template<class T, class Container = deque<T>>
class stack
{
public:
//压栈 -- 栈顶入数据 -- 尾插
void push(const T& x)
{
_con.push_back(x);
}
//出栈 -- 栈顶出数据 -- 尾插
void pop()
{
_con.pop_back();
}
//取栈顶 -- 数据的尾部 -- back()函数
T& top()
{
return _con.back();
}
const T& top() const
{
return _con.back();
}
//判空 -- 对象的empty函数
bool empty() const
{
return _con.empty();
}
//栈大小 -- size函数
size_t size() const
{
return _con.size();
}
private:
Container _con;
};
}
2.2测试函数
//栈的测试
void test_stack()
{
stack<int> st;
//stack<int, vector<int>> st;
//stack<int, list<int>> st;
st.push(1);
st.push(2);
st.push(3);
st.push(4);
while (!st.empty())
{
cout << st.top() << endl;
st.pop();
}
}
3.queue的学习
3.1模拟实现
#pragma once
#include <deque>
namespace apex
{
template<class T, class Container = deque<T>>
class queue
{
public:
//入队列 -- 尾插
void push(const T& x)
{
_con.push_back(x);
}
//出队列 -- 头删
void pop()
{
_con.pop_front();
}
//取队尾 -- back函数
T& back()
{
return _con.back();
}
const T& back() const
{
return _con.back();
}
//取队头 -- front函数
T& front()
{
return _con.front();
}
const T& front() const
{
return _con.front();
}
//判空 -- empty函数
bool empty() const
{
return _con.empty();
}
//队列大小 -- size函数
size_t size() const
{
return _con.size();
}
private:
Container _con;
};
}
3.2测试函数
//队列的测试
void test_queue()
{
queue<int> q;
//queue<int, list<int>> q;
//queue<int, vector<int>> q;
q.push(1);
q.push(2);
q.push(3);
q.push(4);
while (!q.empty())
{
cout << q.front() << endl;
q.pop();
}
}
4.优先级队列的学习
4.0仿函数的引入
/*
仿函数也称作函数对象 -- 它是一个类
在这个类里有公共成员函数 -- ()运算符重载函数
类实例化对象调用它的公共成员函数 -- 就像是在使用普通函数一样
*/
namespace func
{
//例子:
/*
class less
{
public:
bool operator()(const int& left, const int& right) const
{
return left < right;
}
};
*/
//引入模板参数:
template<class T>
class less
{
public:
bool operator()(const T& left, const T& right) const
{
return left < right;
}
};
template<class T>
class greater
{
public:
bool operator()(const T& left, const T& right) const
{
return left > right;
}
};
}
int main()
{
func::less<int> lsFunc;
lsFunc(1, 2); // lsFunc.operator()(1, 2) ;
func::greater<int> gtFunc;
gtFunc(1, 2) ;
return 0;
}
4.1介绍
优先队列
优先级队列是一种容器适配器,根据某种严格的弱排序标准,特别设计为它的第一个元素总是它所包含的元素中的最大元素。
这个上下文类似于堆(heap),在堆中可以随时插入元素,但只能取得堆中最大的元素(优先队列中最上面的那个)。
优先队列是作为容器适配器实现的,容器适配器是使用特定容器类的封装对象作为其底层容器的类,提供一组特定的成员函数来访问其元素。元素从特定容器的“后面”弹出,也就是优先队列的顶部。
底层容器可以是任何标准容器类模板,也可以是其他专门设计的容器类。该容器应可通过随机访问迭代器访问
标准容器类vector和deque可以满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用标准容器向量。
支持随机访问迭代器需要始终在内部保持堆结构。这是由容器适配器自动完成的,在需要时自动调用算法函数make_heap、push_heap和pop_heap。
Priority queue
Priority queues are a type of container adaptors, specifically designed such that its first element is always the greatest of the elements it contains, according to some strict weak ordering criterion.
This context is similar to a heap, where elements can be inserted at any moment, and only the max heap element can be retrieved (the one at the top in the priority queue).
Priority queues are implemented as container adaptors, which are classes that use an encapsulated object of a specific container class as its underlying container, providing a specific set of member functions to access its elements. Elements are popped from the "back" of the specific container, which is known as the top of the priority queue.
The underlying container may be any of the standard container class templates or some other specifically designed container class. The container shall be accessible through random access iterators and support the following operations:
empty()
size()
front()
push_back()
pop_back()
The standard container classes vector and deque fulfill these requirements. By default, if no container class is specified for a particular priority_queue class instantiation, the standard container vector is used.
Support of random access iterators is required to keep a heap structure internally at all times. This is done automatically by the container adaptor by automatically calling the algorithm functions make_heap, push_heap and pop_heap when needed.
4.2例题
class solution
{
public:
int findKthLargest(vector<int>& nums, int k)
{
//创建堆 -- O(N)
priority_queue<int> maxHeap(nums.begin(), nums.end());
//pop k-1 个数据 -- O(k * logN)
while (--k)
maxHeap.pop();
return maxHeap.top();
}
};
4.3模拟实现
#pragma once
namespace apex
{
//仿函数类
template<class T>
class less
{
public:
bool operator()(const T& left, const T& right) const
{
return left < right;
}
};
template<class T>
class greater
{
public:
bool operator()(const T& left, const T& right) const
{
return left > right;
}
};
//template <class T, class Container = vector<T>, class Compare = less<typename Container::value_type> >
template<class T, class Container = vector<T>, class Compare = less<T>>
class priority_queue
{
public:
//构造函数
priority_queue()
{
}
//迭代器构造
template <class InputIterator>
priority_queue(InputIterator first, InputIterator last)
{
while (first != last)
{
_con.push_back(*first);
++first;
}
// 下调建堆法
for (int i = (_con.size() - 1 - 1) / 2; i >= 0; --i)
{
adjust_down(i);
}
}
// 上调NlogN
void adjust_up(size_t child)
{
Compare com;
size_t parent = (child - 1) / 2;
while (child > 0)
{
//if (_con[parent] > _con[child]) //小堆
//if (_con[parent] < _con[child]) //大堆
if (com(_con[parent], _con[child])) //大堆
{
std::swap(_con[child], _con[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
// 下调logN
void adjust_down(size_t parent)
{
Compare com;
size_t child = parent * 2 + 1;
while (child < _con.size())
{
//if (child + 1 < _con.size() && _con[child] > _con[child + 1]) //找real小孩子
//if (child + 1 < _con.size() && _con[child] < _con[child + 1]) //找real大孩子
if (child + 1 < _con.size() && com(_con[child], _con[child + 1])) //找real大孩子
{
++child;
}
//if (_con[parent] > _con[child]) //小堆
//if (_con[parent] < _con[child]) //大堆
if (com(_con[parent], _con[child])) //大堆
{
std::swap(_con[child], _con[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
//入队
void push(const T& x)
{
//尾插数据
_con.push_back(x);
//向上调整
adjust_up(_con.size() - 1);
}
//出队
void pop()
{
//堆顶与堆尾交换
std::swap(_con[0], _con[_con.size() - 1]);
//删除现堆尾(原堆顶)
_con.pop_back();
adjust_down(0);
}
//取队头
const T& top()
{
return _con[0];
}
//判空
bool empty() const
{
return _con.empty();
}
//队大小
size_t size() const
{
return _con.size();
}
private:
Container _con;
};
}
5.测试函数
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <stack>
#include <queue>
#include <vector>
#include <list>
#include <algorithm>
#include <functional>
#include <time.h>
using namespace std;
#include "Stack.h"
#include "Queue.h"
#include "PriorityQueue.h"
//栈的测试
void test_stack()
{
stack<int> st;
//stack<int, vector<int>> st;
//stack<int, list<int>> st;
st.push(1);
st.push(2);
st.push(3);
st.push(4);
while (!st.empty())
{
cout << st.top() << endl;
st.pop();
}
}
//队列的测试
void test_queue()
{
queue<int> q;
//queue<int, list<int>> q;
//queue<int, vector<int>> q;
q.push(1);
q.push(2);
q.push(3);
q.push(4);
while (!q.empty())
{
cout << q.front() << endl;
q.pop();
}
}
//优先级队列的测试
void test_priority_queue() // 默认大的优先级高
{
//无参构造
priority_queue<int> pq;
//有参构造
//explicit priority_queue(const Compare & comp = Compare()
//template<class T, class Container = vector<T>, class Compare = less<T>>
//class priority_queue { } ;
less<int> ls;
std::priority_queue<int> pq0(less<int>());
std::priority_queue<int> pq1(ls);
//迭代器区间构造
int a[] = { 1,2,3,4,5,0 };
priority_queue<int> pq2(a, a + sizeof(a) / sizeof(int));
//传模板参数
//大堆:less<T> 小堆:greater<T>
//template <class T, class Container = vector<T>, class Compare = less<typename Container::value_type> >
priority_queue<int, vector<int>, greater<int>> pq3(a, a + sizeof(a) / sizeof(int));
pq.push(1);
pq.push(2);
pq.push(3);
pq.push(4);
pq.push(5);
pq.push(0);
while (!pq.empty())
{
cout << pq.top() << " ";
pq.pop();
}
cout << endl; //5 4 3 2 1 0
while (!pq2.empty())
{
cout << pq2.top() << " ";
pq2.pop();
}
cout << endl; //0 1 2 3 4 5
}
//vector比deque快大约2倍
void test_op()
{
srand(time(0));
const int N = 100000;
vector<int> v;
v.reserve(N);
deque<int> dp;
for (int i = 0; i < N; ++i)
{
auto e = rand();
v.push_back(e);
dp.push_back(e);
}
int begin1 = clock();
sort(v.begin(), v.end());
int end1 = clock();
int begin2 = clock();
sort(dp.begin(), dp.end());
int end2 = clock();
printf("vector sort:%d\n", end1 - begin1);
printf("deque sort:%d\n", end2 - begin2);
}
int main()
{
test_priority_queue();
return 0;
}