C++ STL 之 deque

本节我们将介绍 STL 中的 deque 容器使用。



deque<T>,是一个定义在 deque 头文件中的容器模板,可以生成包含 T 类型元素的容器,它以双端队列的形式组织元素,因此可以在容器的头部和尾部高效地添加或删除对象,它可以处理先进先出类型的事务,类似于栈这种数据结构,它的使用和 vector 相似,但 vector 只能在容器末尾处增加和删除元素。

deque 容器的初始化

初始化方式:
1.使用默认的构造函数生成 deque 容器,容器中没有任何元素,大小为0,当添加第一个元素,就就会有内存的分配。

deque<string> data;

2.和 vector 容器在本质上相同类似,可以生成给定元素个数的 deque 容器。

deque<string> data(7);

或者

deque<string> data(7,"seven");

3.可以用初始化列表来生成 deque 容器。

deque<string> data {"one","null","three","four","null","six","seven"};

4.也可以用由两个迭代器标识的一段元素来初始化它。

deque<string> data {"one","null","three","four","null","six","seven"};
deque<string> data_ {data};

或者

deque<string> data {"one","null","three","four","null","six","seven"};
deque<string> data_ {begin(data)+2,end(data)-2};

接下来,我们汇总下 deque 容器的初始化。
示例如下:

#include <bits/stdc++.h>
using namespace std;

int main(){
    deque<string> data_1;
    deque<string> data_2(7);
    deque<string> data_3(7,"null");
    deque<string> data_4 {"one","null","three","four","null","six","seven"};
    //全部复制
    deque<string> data_5{data_4};
    auto begin_iter = begin(data_4);
    deque<string>::iterator end_iter = end(data_4);
    begin_iter += 2;
    end_iter -= 2;
    //部分复制
    deque<string> data_6{begin_iter,end_iter};
    cout<<"data_1: "<<data_1.size()<<endl;
    for(auto d : data_1){
        cout<<d<<" ";
    }
    cout<<endl;
    cout<<"data_2: "<<data_2.size()<<endl;
    for(auto d : data_2){
        cout<<d<<" ";
    }
    cout<<endl;
    cout<<"data_3: "<<data_3.size()<<endl;
    for(auto d : data_3){
        cout<<d<<" ";
    }
    cout<<endl;
    cout<<"data_4: "<<data_4.size()<<endl;
    for(auto d : data_4){
        cout<<d<<" ";
    }
    cout<<endl;
    cout<<"data_5: "<<data_5.size()<<endl;
    for(auto d : data_5){
        cout<<d<<" ";
    }
    cout<<endl;
    cout<<"data_6: "<<data_6.size()<<endl;
    for(auto d : data_6){
        cout<<d<<" ";
    }
    cout<<endl;
    return 0;
}

结果如下:

image.png

deque 容器的访问

和 vector 类似,我们可以使用下标索引和 at() 方法进行访问容器内的元素,使用 size() 方法得到容器的大小。
同时 at() 方法比索引方法安全,因为它增加了边界检查,越界时会抛出 out_of_range 异常。
示例如下:

#include <bits/stdc++.h>
using namespace std;

int main(){
    deque<string> data {"one","null","three","four","null","six","seven"};
    cout<<"data: "<<data.size()<<endl;
    for(int i=0;i<data.size();i++){
        cout<<data[i]<<" ";
    }
    cout<<endl;
    cout<<"data: "<<data.size()<<endl;
    //推荐使用 at() 方法
    for(int i=0;i<data.size();i++){
        cout<<data.at(i)<<" ";
    }
    cout<<endl;
    return 0;
}

结果如下:

image.png

deque 成员函数 front() 和 back() 的用法也和 vector 相同。参考之前 vector 章节内容即可。

deque 容器添加和删除元素

我们知道,在 vector 中,有 push_back() 和 pop_back() 方法,可以在 vector 容器的尾部增加和删除元素。
deque 容器不仅能在容器尾部增加和删除元素,在头部也可以增加和删除元素。

deque<string> data {"one","two","three","four"};
//在头部增加一个元素
data.push_front("zeros");
//删除头部的元素
data.pop_front();
//在尾部增加一个元素
data.push_back("five");
//删除尾部的元素
data.pop_back();

除了和 vector —样都有 emplace_back() 函数外,deque 还有成员函数 emplace_front(),可以在序列的开始位置生成新的元素。

deque<string> data {"one","two","three","four"};
string word = "zeros five";
//从word中截取,从6开始的4个字符,在尾部加入
data.emplace_back(word,6,4);
//从word中截取,从0开始的5个字符,在头部加入
data.emplace_front(word,0,5);

和 vector 一样,我们也可以使用 emplace() 或 insert() 在 deque 内部添加或移除元素。
但这个过程相对要慢一些,因为这些操作需要移动容器内现有元素。
emplace():

deque<string> data {"one","two","three","four"};
auto iter = end(data);
//使用 emplace() 容器尾部添加一个 "AAA"
data.emplace(iter,3,'A');
iter = end(data);
iter--;
//在 data 的倒数第一个元素前加上一个 "AAAA"
data.emplace(iter,4,'A');

insert():

deque<string> data {"one","two","three","four"};
deque<string> data_add {"six","seven"};
auto iter = end(data);
string goal = "five";
// 在容器后面增加一个 "five"
data.insert(iter,goal);
iter = begin(data);
iter++;
goal = "one_two";
//在容器第 0 个元素后增加一个 "one_two"
data.insert(iter,goal);
auto begin_iter = begin(data_add);
auto end_iter = end(data_add);
//插入 data_add 的一部分,在 data 尾部插入
iter = end(data);
data.insert(iter,begin_iter,end_iter);

之前系列文章中关于 vector 容器的 insert() 函数的使用也适用于 deque 容器。在 deque 的任意位置插入一个元素会让现有的迭代器全部失效,因此需要重新生成迭代器。
deque 的成员函数clear() , erase() 也和 vector 的相同。
clear():

deque<string> data {"one","two","three","four"};
//清除 deque 容器内的所有元素
data.clear();

erase():

deque<string> data {"null","one","two","three","four","five"};
auto iter = begin(data);
//删除一个元素,由一个迭代器指定位置
data.erase(iter);
//此时容器内的元素为: {"one","two","three","four","five"}
auto begin_iter = begin(data);
auto end_iter = end(data);
//使用两个迭代器,用来指定删除元素的范围
begin_iter += 2;
end_iter -= 1;
data.erase(begin_iter,end_iter);

该部分的内容和 vector 容器大同小异,更详细的可以参考之前关于 vector 方面的博客。

deque 容器修改元素

deque 的成员函数 assign() 可以替换现有的所有元素,它有三个重版版本:

1.替换的新内容是由初始化列表指定的元素
2.替换的新内容是由迭代器指定的一段元素
3.替换的新内容是一个特定对象的多个副本
使用初始化列表
deque<string> data {"one","two","three","four","five"};
auto data_ = {string{"null"},string{"nine"},string{"null"}};
data.assign(data_);

注意,此句

auto data_ = {string{"null"},string{"nine"},string{"null"}};

不能写成

auto data_ = {"null","nine","null"};

因为这么做,data_ 的类型会被推导为 initializer_list<const char*>,然而 assign() 需要的是一个 initializer_list<string> 类型的实参,这样就无法通过编译。
当然,我们可以在 assign() 的实参中直接定义初始化列表

deque<string> data {"one","two","three","four","five"};
data.assign({"null","nine","null"});

这样,data 的内容

{"one","two","three","four","five"}

将会被初始化成 data_ 的内容

{"null","nine","null"}
由迭代器指定的一段元素
deque<string> data {"one","two","three","four","five"};
deque<string> data_ {"never","hardly ever","sometimes","often","usually","always"};
deque<string>::iterator begin_iter = begin(data_);
auto end_iter = end(data_);
begin_iter++;
end_iter--;
data.assign(begin_iter,end_iter);

此时,data 的内容为:

{"hardly ever","sometimes","often","usually"}
一个特定对象的多个副本

assign() 中第一个参数指定了替换当前容器内容的第二个参数的个数

deque<string> data {"one","two","three","four","five"};
data.assign(5,"null");

此时,data 的内容为:

{"null","null","null","null","null"}

最后,我们来看看容器的赋值操作,只要赋值运算的右操作数必须和左操作数是相同类型,就可以完成赋值操作。
示例如下:

#include <bits/stdc++.h>
using namespace std;

int main(){
    deque<string> data {"one","two","three","four","five"};
    deque<string> data_;
    data_ = data;
    cout<<"data: "<<data.size()<<endl;
    for(auto d : data){
        cout<<d<<" ";
    }
    cout<<endl;
    cout<<"data_: "<<data_.size()<<endl;
    for(auto d : data_){
        cout<<d<<" ";
    }
    cout<<endl;
    //data 的元素发生更改
    data = {"hardly ever","sometimes","often","usually"};
    cout<<"data: "<<data.size()<<endl;
    for(auto d : data){
        cout<<d<<" ";
    }
    cout<<endl;
    cout<<"data_: "<<data_.size()<<endl;
    for(auto d : data_){
        cout<<d<<" ";
    }
    cout<<endl;
    return 0;
}

结果如下:

image.png

至此,deque 的内容暂告一段落了,可以看出,deque 的使用和 vector 其实大同小异,它的特点之一是可以在容器的头部和尾部增加元素,当然由于移动元素,时空代价会略有增加。
deque 容器的部分细节和 vector 很相似,例如删除元素后,迭代器的变化,在之前的博客中也有所介绍,这里就不详细展开了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容