引言
小伙伴们大家好,欢迎回到我们的STL系列教程!在前两篇文章中,我们分别学习了string和vector容器,它们各自在C++编程中扮演着重要的角色。
今天,我们将要介绍的是STL中的另一位成员——deque容器。deque(发音为deck),全称是double-ended queue,即双端队列。它允许在容器的两端进行快速的插入和删除操作,这是deque与vector的一个重要区别。deque的这种特性使得它在某些场景下比vector更加高效。那么,让我们开始探索deque容器的独特魅力和实际应用吧!
deque容器
1.deque概念
deque是double-ended-queue的缩写,deque也是连续的存储空间,跟vector一样。它们都支持通过下标索引来取值。
deque和vector的区别:
1.deque是两端开口,头尾两端都有push和pop方法,vector只有尾端开口。
2.deque是由多段连续的空间组合而成的,它可以随时增加一段空间连接起来。所以deque不会存在vector由于空间不足而去动态拓展的问题。
所以deque也就没有容量capacity的概念。deque通过内部的一个中继器来维护多端连续的空间。
补充:打印函数模板
我们在每次对容器内不同的输入输出内容的类型,我们都需要对其进行重载函数,我们既然都学过了模板化技术,我们就直接使用模板化技术来写一个打印函数模板。
- 代码示例:
//打印函数模板
template<class Type>
void printTest(deque<Type>& v)
{
//for (deque<Type>::iterator it = v.begin(); it != v.end(); it++)
/*
不能用迭代器因为这里的iterator在模板里不能识别类型,
可以直接使用auto去匹配类型(C++11之后的特性)
*/
cout << &v << ":";
for (auto it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
2.deque构造函数
- 函数原型:
deque<T> deq;
//默认构造形式
deque(beg, end);
//将参数deque[beg, end)
区间中的元素拷贝。
deque(n, elem);
//将n个elem拷贝。
deque(const deque &deq);
//拷贝构造函数 - 代码示例:
//deque构造函数
void test01()
{
//普通构造一个容器deq;
deque<int> deq;
deq.push_back(1);
deq.push_back(3);
deq.push_back(5);
deq.push_back(7);
deq.push_back(9);
deq.push_back(2);
deq.push_back(4);
deq.push_back(6);
printTest(deq);
//拷贝构造了容器deq2的一部分;
deque<int> deq2(deq.begin(),deq.end()-3);
printTest(deq2);
//开辟一个容器并放入5个10;
deque<int> deq3(5, 10);
printTest(deq3);
//拷贝构造整个
deque<int> deq4(deq3);
printTest<int>(deq4);
}
3.deque赋值操作
- 函数原型:
deque& operator=(const deque &deq);
//重载赋值运算符
assign(beg, end);
//将参数deque[beg, end)区间中的数据赋值
assign(n, elem);
//将n个elem赋值 - 代码示例:
//deque构造函数
void test02()
{
deque<double> t;
t.push_back(0.23);
t.push_back(1.23);
t.push_back(2.23);
t.push_back(3.23);
t.push_back(4.23);
t.push_back(5.23);
printTest(t);
deque<double> t2=t;
printTest(t2);
deque<double> t3;
t3.assign(3, 0.23);
printTest(t3);
deque<double> t4;
t4.assign(t2.begin(), t2.end() - 1);
printTest(t4);
}
4. deque大小操作
函数原型:
empty();
//判断容器是否为空
size();
//返回容器中元素的个数
resize(num);
//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超
出容器长度的元素被删除。
resize(num, elem);
//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则
末尾超出容器长度的元素被删除。
- 代码示例:
//deque大小操作
void test03()
{
deque<char> s;
s.push_back('a');
s.push_back('b');
s.push_back('c');
s.push_back('d');
s.push_back('e');
s.push_back('a');
cout << s.empty() << endl;
cout << s.size() << endl;
cout << s.max_size() << endl;
s.resize(3);
printTest(s);
s.resize(10);
printTest(s);
//当大小变大时,会自动添加默认值,字符型的默认值时‘ ’。
s.resize(15, 't');
printTest(s);
s.resize(13);
printTest(s);
}
5.deque 插入和删除
函数原型:
两端插入操作:
push_back(elem);
//在容器尾部添加一个数据
push_front(elem);
//在容器头部插入一个数据
pop_back();
//删除容器最后一个数据
pop_front();
//删除容器第一个数据
指定位置操作:
insert(pos,elem);
//在pos位置插入一个elem元素的拷贝,返回新数据的位置。
insert(pos,n,elem);
//在pos位置插入n个elem数据,返回新数据的位置。
insert(pos,beg,end);
//在pos位置插入另一个deque[beg,end)区间的数据,返回新数据的位置。
erase(beg,end);
//删除[beg,end)区间的数据,返回下一个数据的位置。
erase(pos);
//删除pos位置的数据,返回下一个数据的位置。
clear();
//清空容器的所有数据
- 代码示例:
//deque 插入和删除
void test04()
{
//两端插入操作:
/*
push_back(elem); //在容器尾部添加一个数据
push_front(elem); //在容器头部插入一个数据
pop_back(); //删除容器最后一个数据
pop_front(); //删除容器第一个数据
*/
cout << "两端操作如下****" << endl;
deque<int> e;
e.push_back(1);
e.push_front(2);
e.push_back(3);
printTest(e);
e.pop_back();
printTest(e);
e.pop_front();
printTest(e);
//指定位置操作:
/*
insert(pos,elem); //在pos位置插入一个elem元素的拷贝,返回新数据的位置。
insert(pos,n,elem); //在pos位置插入n个elem数据,返回新数据的位置。
insert(pos,beg,end); //在pos位置插入另一个deque[beg,end)区间的数据,返回新数据的位置。
erase(beg,end); //删除[beg,end)区间的数据,返回下一个数据的位置。
erase(pos); //删除pos位置的数据,返回下一个数据的位置。
clear(); //清空容器的所有数据
*/
cout << "指定位置操作如下****" << endl;
deque<int> f;
e.insert(e.begin() + 1, 4);
printTest(e);
e.insert(e.begin() + 2, 2,5);
printTest(e);
f.push_back(6);
f.push_back(7);
f.push_back(8);
f.push_back(9);
e.insert(e.begin() + 2,f.end()-2,f.end());
printTest(e);
}
6.deque迭代器失效问题
-
deque跟vector一样,都属于序列式容器,以下这些操作会导致部分迭代器失效:
1.deque首尾两端插入元素的时候,不会导致动态拓展,所以只会导致首尾两端的迭代器失效,其他位置的元素不需要移动。
2.在首尾两端删除元素的时候,也不会导致其他元素移动,只会造成被删除元素当前的迭代期失效。
3.在除首尾两端的其他位置插入或者删除元素,会导致部分迭代器失效。
所以,当有迭代器失效的时候,仍然需要做迭代器更新。 -
代码示例:
// deque迭代器失效
void test05()
{
deque<int> v;
v.push_back(10);
v.push_back(30);
v.push_back(10);
v.push_back(20);
v.push_back(10);
v.push_back(50);
v.push_back(60);
v.push_back(30);
v.push_back(100);
printTest(v);
//遍历删除容器内30的数
//要求删除所有30的元素
//遍历整个数组,如果发现是30,就删掉,并在这个位置放上66.
for (deque<int>::iterator it = v.begin(); it != v.end(); it++)
{
if (*it==30)
{
it=v.erase(it);
it = v.insert(it, 66);
}
}
printTest(v);
v.clear();
printTest(v);
}
7.deque 数据存取
函数原型:
at(int idx);
//返回索引idx所指的数据
operator[];
//返回下标索引所指的数据
front();
//返回容器中第一个数据元素
back();
//返回容器中最后一个数据元素
- 代码示例:
//deque 数据存取
void test06()
{
deque<int> h;
h.push_back(12);
h.push_back(13);
h.push_back(14);
h.push_back(15);
printTest(h);
cout <<"h.at(2):" <<h.at(2) << endl;
cout <<"h[0]:"<< h[0] << endl;
cout << "h.front() :"<<h.front() << endl;
cout << "h.back()"<<h.back()<<endl;
}
8.元素互换
可以实现两个deque容器元素的互换
d1.swap(d2);
//d1和d2互换元素
- 代码示例:
//deque容器元素交换
void test07()
{
deque<int> d1;
deque<int> d2;
d1.push_back(2);
d1.push_back(2);
d1.push_back(2);
d2.push_back(3);
d2.push_back(3);
d2.push_back(3);
printTest(d1);
printTest(d2);
d1.swap(d2);
printTest(d1);
printTest(d2);
}
9.deque总结:
- deque的数据结构决定了它在做两端操作的时候效率最高,但是做其他位置元素的操作的时候,效率也比较低。
- deque和vector都是连续空间的结构,支持下标取值,做遍历查询效率高。
结语
随着对deque容器的深入了解,我们现在应该能够感受到它在处理需要频繁在两端进行操作的数据时的优势。deque的双端操作能力,使得它在某些算法实现和数据结构设计中显得尤为重要。虽然deque的使用频率可能没有vector那么高,但它在特定场景下的作用是不可替代的。
在接下来的学习中,我们还会遇到更多STL容器,每个都有它独特的用途和特性。希望你能继续跟随小杨的步伐,一起探索STL的丰富世界。如果你对deque容器有任何疑问,或者在使用过程中遇到了难题,欢迎在评论区留言交流。我们下期再见,继续我们的STL学习之旅!
小伙伴们加油呀!冲冲冲!!!!!