C++ STL中的deque
(双端队列)是一种双开口的,拥有连续储存空间的数据结构。双开口意思就是说既可以在头部进行插入删除元素,又可以在尾部进行插入删除元素。它结合了vector和list的优点。
(1)与vector相比:vector没有头插头删的操作,而deque有!
(2)与list相比:list没有连续的存储空间,而deque有!
deque的特性
双端操作:deque支持在前端和后端执行快速的插入和删除操作,使其成为处理需要在两端进行频繁操作的问题的理想选择
随机访问:与list不同,deque允许在常量时间内对元素进行随机访问。这是因为deque的内部结构采用分段数组,而不是链表,从而提供了更好的随机访问性能。
动态扩展:deque的大小可以动态调整,无需事先分配固定大小的内存。这使得deque适用于需要动态增长和缩小的情况。
保持迭代器有效性:deque在进行插入和删除操作时,能够更好地保持迭代器的有效性。这意味着在进行这些操作后,不会导致所有迭代器失效。
内存局部性:deque的内部结构利用了多个缓冲区,有助于提高内存局部性,从而在某些情况下提供更好的性能。
举例说明,关于随机访问:
在C++中,std::deque允许在常量时间内对元素进行随机访问,这意味着可以通过索引直接访问deque中的元素,而不会随deque的大小而增加访问时间。下面是一个简单的例子来说明这一点:
#include <iostream>
#include <deque>
int main() {
// 创建一个包含一些元素的deque
std::deque<int> myDeque = {1, 2, 3, 4, 5};
// 在常量时间内对元素进行随机访问
std::cout << "Element at index 2: " << myDeque[2] << std::endl;
std::cout << "Element at index 4: " << myDeque[4] << std::endl;
return 0;
}
在上面的例子中,myDeque[2]
和 myDeque[4]
的访问时间是常量的,不受deque的大小的影响。
这是因为std::deque使用了分段数组的结构,每个分段都是一个固定大小的数组,因此可以直接计算索引的位置,而不需要遍历整个容器。
请注意,虽然std::deque允许在常量时间内对元素进行随机访问。
但与std::vector相比,std::deque在访问元素时可能涉及更多的间接层次,因此在一些情况下,std::vector的随机访问性能可能略优。
在选择容器时,需要根据具体的使用情况和需求权衡不同容器的优劣。
STL标准库中deque的基本用法
//要使用deque,首先需要包含相关的头文件:
#include <deque>
//接下来,可以声明一个deque对象,并开始使用它:
std::deque<int> myDeque;
//可以使用push_front和push_back在deque的前端和后端插入元素:
myDeque.push_front(1);
myDeque.push_back(2);
//使用pop_front和pop_back从deque的前端和后端删除元素:
myDeque.pop_front();
myDeque.pop_back();
deque工作原理
手绘有点丑将就看吧,后期再更新= _=
- 无论是
push_front
还是push_back
, 都需要判断当前数组是否已满, 已满的话将其扩容为原容量的2倍, 将原来的数组元素复制到新的数组中。 push_front
需要将frontIndex
自减后在frontIndex
位置插入,frontIndex
自减后需要加上capacity
后对capacity
取模, 这使得如果frontIndex
为负,frontIndex
将指向从数组末尾。push_back
直接在backIndex
位置插入, 然后将backIndex
自增,backIndex
自增后需要对capacity
取模, 这使得如果backIndex
越界,backIndex
将指向从数组开始的位置
实现deque
#include <iostream>
#include <sstream>
#include <algorithm>
#include <string>
#include <stdexcept>
template<typename T>
class Deque
{
private:
T* elements;//就是一个循环一维数组
size_t capacity;//数组的容量
size_t frontIndex;//deque的前端索引
size_t backIndex;//deque的后端索引
size_t size;//deque中元素的数量
public:
//构造函数
Deque()
:elements(nullptr)
,capacity(0)
,frontIndex(0)
,backIndex(0)
,size(0)
{}
~Deque() {
clear();
delete[] elements;
}
//再deque的前端插入元素
void push_front(const T& value) {
//检查是否需要扩容
if (size == capacity) {
resize();
}
//计算新的前端索引
frontIndex = (frontIndex - 1 + capacity) % capacity;
// 在新的前端位置插入元素
elements[frontIndex] = value;
//增加deque的元素
++size;
}
/*
* 在队列前端插入元素。首先检查容量并调整数组大小(如果需要)
然后更新 frontIndex 并在新位置插入元素,最后增加 size。
frontIndex = (frontIndex - 1 + capacity) % capacity;是为了防止数组索引为负, 使其指向尾端。
*/
//在deque后端加入插入元素
void push_back(const T& value) {
//检查是否需要扩容
if (size == capacity) {
resize();
}
//在新的后端插入位置元素
elements[backIndex] = value;
//计算新的后端索引
backIndex = (backIndex + 1) % capacity;
//增加deque的元素
++size;
}
/*
在队列后端插入元素。同样先检查容量并调整数组大小,然后直接在 backIndex 插入元素,
更新 backIndex,最后增加 size。
backIndex = (backIndex + 1) % capacity;是为了防止索引越界, 使其回到数组开始的位置。
*/
//从deque的前端移除元素
void pop_front() {
//检查deque是否为空
if (size == 0) {
throw std::out_of_range("Deque is empty");
}
frontIndex = (frontIndex + 1) % frontIndex;
--size;
}
//从deque的后端移除元素
void pop_back() {
//检查deque是否为空
if (size == 0) {
throw std::out_of_range("Deque is empty");
}
backIndex = (backIndex - 1 + capacity) % capacity;
--size;
}
//随机访问元素
T& operator[](int index) {
if (index < 0 || index >= size) {
throw std::out_of_range("Index out of range");
}
return elements[(index + frontIndex) % capacity];
}
//获取deque中的元素数量
size_t getSize() const() {
return size;
}
// 清空deque
void clear() {
while (size > 0) {
pop_front();
}
}
//打印deque的元素
void printElement() const {
size_t index = frontIndex;
for (size_t i = 0; i < size; ++i) {
std::cout << elements[index] << " ";
index = (index + 1) % capacity;
}
std::cout << std::endl;
}
private:
void resize() {
//计算新的容量
size_t newCapacity = (capacity == 0) ? 1 : capacity * 2;
//创建新的数组
T* newElements = new T[newCapacity];
//复制元素到新数组
for (size_t i = 0; i < size; ++i) {
newCapacity[i] = elements[index];
index = (index + 1) % capacity;
}
//释放就数组的内存
delete[] elements;
//更新成员变量
elements = newElements;
capacity = newCapacity;
frontIndex = 0;
backIndex = size;
}
};
int main() {
// 创建一个 Deque 对象
Deque<int> myDeque;
int N;
std::cin >> N;
// 读走回车
getchar();
std::string line;
// 接收命令
for (int i = 0; i < N; i++) {
std::getline(std::cin, line);
std::istringstream iss(line);
std::string command;
iss >> command;
int value;
if (command == "push_back") {
iss >> value;
myDeque.push_back(value);
}
if (command == "push_front") {
iss >> value;
myDeque.push_front(value);
}
if (command == "pop_back") {
if (myDeque.getSize() == 0) {
continue;
}
myDeque.pop_back();
}
if (command == "pop_front") {
if (myDeque.getSize() == 0) {
continue;
}
myDeque.pop_front();
}
if (command == "clear") {
myDeque.clear();
}
if (command == "size") {
std::cout << myDeque.getSize() << std::endl;
}
if (command == "get") {
iss >> value;
std::cout << myDeque[value] << std::endl;
}
if (command == "print") {
if (myDeque.getSize() == 0) {
std::cout << "empty" << std::endl;
}
else {
myDeque.printElements();
}
}
}
return 0;
}