C++集锦二 C++标准库中几个常见的数据结构
来源:互联网 发布:初请失业金数据公布网 编辑:程序博客网 时间:2024/06/08 10:20
set类型
set容器只是单纯的键的集合,键必须为一。set容器不支持下标操作,而且没有定义maped_type类型。在set容器中,value_type不是pair类型,而是与key_type类型相同的类型。
1.在set中添加元素
set<string> set1;
set1.insert("the");
set1.insert("end");
也可以添加一对迭代器,如下:
set1.insert(set33.begin(),set33.end());
这个方法返回void
带有一个键参数的insert版本返回pair类型对象,包含一个迭代器和bool值,迭代器指向拥有该键德曼元素,而bool值则表明是否添加了该元素。
2.从set中获取元素
set1.find(sting ss);方法返回指向在set1中ss的迭代器
set1.count(string ss);返回的值只可能是0或者1(0表示不存在,1表示存在).
set中的键为const类型。
pair
与关联容器相关的模板类型,包含两个数据成员,在utility头文件中定义.
pair类型提供的操作:
pair<T1,T2> p1;
pair<T1,T2> p1(v1,v2);
make_pair(v1,v2);
p1 < p2;
p1 == p2;
p.first;
p.second;
1.创建和初始化:
pair<string,string> name;
pair<string,vector<int>> data;
以上全部调用pair类型的默认构造函对其成员进行数值初始化,成员初始化为空或0值
也能在定义时提供初始化式:
pair<string,int> Mike("Mike Brown",26);
技巧:使用typedef简化声明
typedef pair<string,string> name;
name dotcom("is","programmer");
2.pair对象的操作
pair的两个成员--first和second都是公有的,使用点操作符就可以访问其成员:
string next;
if (name.first == "gem" && name.second == "linux")
next = name.first;
3.生成新的pair对象
make_pair函数可以生成pair对象,例子:
pair<string,string> next_auth;
string first,last;
while (cin >> first >> last){
next_auth = make_pair(first,last);
}
技巧:可以直接用标准输入流读入数据到pair对象中
pair<string,string> next_auth;
while (cin >> next_auth.first >> next_auth.second){...}
map类型
map是键-值对的集合。map类型通常可以理解为关联数组:可以使用键作为下标获取一个值,正如内置数组一样。而关联的本质在于元素的值于某个特定的键相关联,而并非通过元素在数组中的位置获取。
1.map对象的定义
要使用map对象,则必须包含map头文件。在定义map对象时,必须分别指明键和值得类型
map<string,int> word_count;
string为键,int为值
map的构造函数
map<k,v> m; //创建一个名为m的空map对象,其键和值得类型分别为k和v
map<k,v> m(m2); //创建m2的副本m,m与m2必须有相同的键类型和值类型
map<k,v> m(b,e) //创建map类型的对象m,存储迭代器b和e标记范围内所有元素的副本。元素的类型必须能转换为pair<const k,v>。
2.map定义的类型
map<k,v>::key_type 在map容器中用作索引的键的类型
map<k,v>::maped_type 在map容器中,键所关联的值得类型
map<k,v>::value_type 一个pair类型,它的first元素具有const map<k,v>::key_type类型,而second元素则为map<k,v>::maped_type类型。
对map迭代器进行解引用将获得一个pair对象,它的first成员存放键,为const,而second成员则存放值。
3.给map添加元素
与其他下标操作符一样,map的下标也是使用索引获取该键关联的值。如果该键在容器中,则map的下标运算与vector的下标元素一样:返回该键所关联的值。只有在所查的键不存在是,map容器才为该键创建一个新的元素,并将它插入到此map对象中。此时,所关联的值采用值初始化:类类型的元素用默认的构造函数初始化,而内置类型的元素则初始化为0。
map容器提供的insert操作
m.insert(e) e是在m上的value_type类型的值。如果键(e.first)不在m中,则插入一个e.second的新元素;如果该键在m中已经存在,则保持m不变。该函数返回一个pair类型对象,包含指向键为e.first的元素的map迭代器,以及一个bool类型的对象,表示是否插入了该元素
m.insert(beg,end) 迭代器之间的元素必须为value_type类型的键-值对。在该范围内的元素,如果不在m中,则插入。
m.insert(iter,e) 从迭代器iter开始寻找,不存在m中则插入,返回迭代器,指向m中具有指定键的元素
word_count.insert(map<string,int>::value_type("Anna",1));
也可以简化为 word_count(make_pair("Anna",1));
使用第一种方法插入返回的是pair类型。
pair<map<string,int>::iterator,bool> ret=word_count.insert(map<string,int>::value_type("Anna",1));
4.查找并获取map中的元素
使用下标获取map中国的值,如果该键不存在,则插入了显得元素。下面map提供了两个操作
m.count(k) 返回m中k出现的次数
m.find(k) 存在则返回指向该元素的迭代器,否则返回超出末端迭代器
对于map,count的返回值只可能是0或者1,通过该函数就可以知道是否存在,然后使用下标操作,则不会出现不存在则插入新元素的问题。
对于find函数,通过判断是否与超过末端的迭代器相等判断是否存在,若存在则使用返回的迭代器,该返回的迭代器是pair类型,通过下面的方法获得值int value=iter->second;
5.从map中删除元素
m.erase(k) 删除m中键为k的元素。返回size_type类型的值,表示删除元素的个数
m.erase(p) 从m中删除迭代器p所指向的元素。p必须指向m中确实存在的元素,而且不能等于m.end()。返回void类型
m.erase(b,e) 从m中删除一段范围内的元素,该范围由迭代器对b和e标记。b和e必须标记m中一段有效范围:即b和e都必须指向m中的元素或者最后一个元素的下一个位置。而且b和e要么相等(此时删除的范围为空),要么b指向的元素必须出现在e所指向的元素之前。返回void类型。
除此之外,map还提供一种特殊的erase操作,其参数是key_type类型的值,如果拥有该元素的键存在,则删除该元素,返回删除的个数。对于map来说,返回值为1或者0。0表示表示删除的元素在map中不存在。
对于map对象的遍历,可以使用迭代器,m.end()和m.begin()。
vector
vector是同一种对象的集合,每个对象都有一个对应的整数索引值。和string对象一样,标准库将负责管理与存储元素相关的类存。引入头文件
#include<vector>
1.vector对象的定义和初始化
如果没有指定元素的初始化式,那么标准库将自行提供一个元素初始值进行值初始化。这个由库生成的初始值将用来初始化容器中的每个元素,具体的值为何,取决于存储在vector中元素的数据类型。
如果vector保存内置类型如int,那么标准库将用0值创建元素初始化式
vector<int> vv(10); //10个元素,每个被初始化为0
如果vector保存的是含有构造函数的类类型的元素,标准库将用该类型的默认的构造函数创建元素的初始化式
vector<string> vvvv(10) //10个元素,每个被初始化为空字符串
还有第三种情况,元素类型可能是没有定义构造函数的类类型。这种情况下,标准库仍产生一个带初始值的对象,这个对象的每个成员进行了值初始化。
2.vector对象的操作
几种重要的操作
size()返回相应vector类定义的size_type的值。
vector::size_type //error
vector<int>::size_type //ok
push_back()函数将新元素添加到vector最后面。
3.迭代器简介
除了使用下标来访问vector对象的元素外,标准库还提供了访问元素的方法:使用迭代器。迭代器是一种检查容器内元素并且遍历元素的数据类型。
1.容器的iterator类型
每种容器类型都定义了自己的迭代器类型,如vector:
vector<int> ::iterator iter;变量名为iter。
2.begin和end操作
每种容器都定义了一队命名为begin和end的函数,用于返回迭代器。如果容器中有元素的话,由begin返回的元素指向第一个元素。
vector<int>::iterator iter=v.begin();
若v不为空,iter指向v[0]。
由end返回的迭代器指向最后一个元素的下一个,不存在,若v为空,begin和end返回的相同。
*iter=0;
iter++即将迭代器向前移动一个位置
即将v[0]赋值为0,由end返回的不允许进行解操作。
==和!=操作符来比较两个迭代器,若两个迭代器指向同一个元素,则它们相等,否则不想等。
迭代器使用举例:
for(vector<int>::iterator iter=v.begin();iter!=v.end();iter++)
*iter=0;
将vector中的元素全部赋值为0;
3.vector的几种遍历方式比较
方式一
for (size_t i =0; i < vec.size(); i ++) {
int d = vec[i];
}
方式二
size_t len = vec.size();
for (size_t i =0; i < len; i ++) {
int d = vec[i];
}
方式三for (auto it = vec.begin(); it != vec.end(); it ++) {
int d = *it;
}
testBianli1 all running duration:3.440000(ms)
testBianli2 all running duration:2.854000(ms)
testBianli3 all running duration:11.009000(ms)
很明显了,第二种方式是最快的。个人觉得原因如下:
第一种方法每次都要调用size()函数去计算vec的长度。通过查看size(),其实现如下:
_LIBCPP_INLINE_VISIBILITY
size_type size() const_NOEXCEPT
{return static_cast<size_type>(this->__end_ -this->__begin_);}
指针地址相减,再来一个强制转换最后得到size()。就这这里稍微费点时间。
第三种方式是最费时间的。使用迭代器it循环,迭代器本身不是内部数据,它的各种操作(比较,偏移,取值操作)都是一系列内联函数操作,暗地里干的事远比看到的复杂。这个迭代器给自己套上伪装,让你可以像使用指针一样利用它去访问对象,但是毕竟中间隔了一层。个人觉得迭代器的实用主要是便于stl中算法的实现,有一种通用的数据类型来访问各种容器中的元素。
4.算法
(1)使用reverse将元素翻转:需要头文件#include<algorithm>
, reverse(vec.begin(),vec.end());
将元素翻转(在vector中,如果一个函数中需要两个迭代器,一般后一个都不包含)。
(2)使用sort排序:需要头文件#include<algorithm>
, sort(vec.begin(),vec.end());
(默认是按升序排列,即从小到大)。
可以通过重写排序比较函数按照降序比较,如下:
定义排序比较函数:
调用时:sort(vec.begin(),vec.end(),Comp)
,这样就降序排序。
vector和built-in数组类似,它拥有一段连续的内存空间,并且起始地址不变,因此它能非常好的支持随即存取,即[]操作符,但由于它的内存空间是连续的,所以在中间进行插入和删除会造成内存块的拷贝,另外,当该数组后的内存空间不够时,需要重新申请一块足够大的内存并进行内存的拷贝。这些都大大影响了vector的效率。
list就是数据结构中的双向链表(根据sgi stl源代码),因此它的内存空间可以是不连续的,通过指针来进行数据的访问,这个特点使得它的随即存取变的非常没有效率,因此它没有提供[]操作符的重载。但由于链表的特点,它可以以很好的效率支持任意地方的删除和插入。
deque是一个double-ended queue,它的具体实现不太清楚,但知道它具有以下两个特点:
它支持[]操作符,也就是支持随即存取,并且和vector的效率相差无几,它支持在两端的操作:push_back,push_front,pop_back,pop_front等,并且在两端操作上与list的效率也差不多。
Map是STL的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据处理能力,由于这个特性map内部的实现自建一颗红黑树(一种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能。
set是集合,set中不会包含重复的元素,这是和vector的第一个区别,第二个区别是set内部用平衡二叉树实现,便于元素查找,而vector是使用连续内存存储,便于随机存取。
因此在实际使用时,如何选择这几个容器中哪一个,应根据你的需要而定,一般应遵循下面
的原则:
1、如果你需要高效的随即存取,而不在乎插入和删除的效率,使用vector
2、如果你需要大量的插入和删除,而不关心随即存取,则应使用list
3、如果你需要随即存取,而且关心两端数据的插入和删除,则应使用deque。
4、如果你要存储一个数据字典,并要求方便地根据key找value,那么map是较好的选择
5、如果你要查找一个元素是否在某集合内存中,则使用set存储这个集合比较好
- C++集锦二 C++标准库中几个常见的数据结构
- CRC16常见几个标准的算法及C语言实现
- C语言中常见的C的标准库函数
- 常见c语言的标准库
- 面试中常见的C/C++问题集锦
- 几个常见的C函数
- C标准库常见函数
- C中几个常见的测试字符的函数
- c语言中常见的几个段错误
- C语言标准库中常见字符串处理函数
- 几个不熟悉的标准C的基础库
- c语言中几个常见排序
- ANSI C标准中几个标准预定义宏
- ANSI C标准中有几个标准预定义宏:
- ANSI C标准中几个标准预定义宏
- ANSI C标准中有几个标准预定义宏(也是常用的):
- C 标准库(二)
- C语言标准库常见函数的内部实现
- 从零开始前端学习[45]:js中的所谓的事件类型,鼠标事件,表单事件,键盘事件以及系统事件
- 一个简单Class文件的分析
- 用文本文档进行输入输出
- 笔试题
- 折半查找算法
- C++集锦二 C++标准库中几个常见的数据结构
- 战争迷雾制作思路
- 数理逻辑模拟-2
- GhostXP安装时没有D盘问题
- The Matrix
- HTML
- 【算法复习】 树链剖分
- 实现选择功能的那些步骤
- HashMap面试题:90%的人回答不上来