C++容器——zhuan

容器简介

STL标准容器类简介
标准容器类      说明
顺序性容器
vector       相当与数组,从后面快速的插入与删除,直接访问任何元素
deque       双队列,从前面或后面快速的插入与删除,直接访问任何元素
list       双链表,从任何地方快速插入与删除
关联容器
set       快速查找,不允许重复值
multiset      快速查找,允许重复值
map       一对一映射,基于关键字快速查找,不允许重复值
multimap      一对多映射,基于关键字快速查找,允许重复值
容器适配器
stack       后进先出
queue       先进先出
priority_queue      最高优先级元素总是第一个出列

所有标准库共有函数
默认构造函数      提供容器默认初始化的构造函数。
复制构造函数      将容器初始化为现有同类容器副本的构造函数
析构函数      不再需要容器时进行内存整理的析构函数
empty       容器中没有元素时返回true,否则返回false
max_size      返回容器中最大元素个数
size       返回容器中当前元素个数
operator=      将一个容器赋给另一个容器
operator<      如果第一个容器小于第二个容器,返回true,否则返回false,
operator<=      如果第一个容器小于或等于第二个容器,返回true,否则返回false
operator>      如果第一个容器大于第二个容器,返回true,否则返回false
operator>=      如果第一个容器大于或等于第二个容器,返回true,否则返回false
operator==      如果第一个容器等于第二个容器,返回true,否则返回false
operator!=      如果第一个容器不等于第二个容器,返回true,否则返回false
swap       交换两个容器的元素

其中operator>,operator>=,operator<,operator<=,operator==,operator!=均不适用于priority_queue

顺序容器和关联容器共有函数
begin      该函数两个版本返回iterator或const_iterator,引用容器第一个元素
end      该函数两个版本返回iterator或const_iterator,引用容器最后一个元素后面一位
rbegin      该函数两个版本返回reverse_iterator或const_reverse_iterator,引用容器最后一个元素
rend      该函数两个版本返回reverse_iterator或const_reverse_iterator,引用容器第一个元素前面一位
erase      从容器中清除一个或几个元素
clear      清除容器中所有元素

下表显示了顺序容器和关联容器中常用的typedef,这些typedef常用于变量、参数和函数返回值的一般性声明。
value_type      容器中存放元素的类型
reference      容器中存放元素类型的引用
const_reference 容器中存放元素类型的常量引用,这种引用只能读取容器中的元素和进行const操作
pointer      容器中存放元素类型的指针
iterator      指向容器中存放元素类型的迭代器
const_iterator      指向容器中存放元素类型的常量迭代器,只能读取容器中的元素
reverse_iterator      指向容器中存放元素类型的逆向迭代器,这种迭代器在容器中逆向迭代
const_reverse_iterator      指向容器中存放元素类型的逆向迭代器,只能读取容器中的元素
difference_type      引用相同容器的两个迭代器相减结果的类型(list和关联容器没有定义operator-)
size_type      用于计算容器中项目数和检索顺序容器的类型(不能对list检索)

容器的比较

vector      (连续的空间存储,可以使用[ ]操作符)快速的访问随机的元素,快速的在末尾插入元素,但是在序列中间岁间的插入,删除元素要慢,而且如果一开始分配的空间不够

的话,有一个重新分      配更大空间,然后拷贝的性能开销
deque (小片的连续,小片间用链表相连,实际上就是一个每小片指针的数组,因为知道类型,所以还是可以使用【】,只是速度没有vector快)快速的访问随机的元素,快速

的在开始和末尾插入元素,随机的插入,删除元素要慢,空间的重新分配要比vector快
list       (每个元素间用链表相连)访问随机元素不如vector快,随机的插入元素比vector快,对每个元素分配空间,所以不存在空间不够,重新分配的情况

set 内部元素唯一,用一棵平衡树结构来存储,因此遍历的时候就排序了,查找也比较快的哦。
map 一对一的映射的结合,key不能重复。

1、vector
    连续存储结构,每个元素是在内存上是连续的;
    支持高效的随机访问和在尾端插入/删除操作,但其他位置的插入/删除操作效率低下;
2、deque
    连续存储结构,即其每个元素在内存上也是连续的,类似于vector,不同之处在于,deque提供了两级数组结构,第一级完全类似于vector,代表实际容器;另一级维护容器的首位地址。
   这样,deque除了具有vector的所有功能外,还支持高效的首端插入/删除操作。
3、list
    非连续存储结构,具有双链表结构,每个元素维护一对前向和后向指针,因此支持前向/后向遍历。
    支持高效的随机插入/删除操作,但随机访问效率低下,且由于需要额外维护指针,开销也比较大。

    list支持push_front(),push_back(),pop_front(),pop_back() 
    list不支持'[]'和at(),因为它不是连续存放的. 
    标准模板库里比大小都用<,比等于用== 


4、vector V.S. list V.S. deque:
    a、若需要随机访问操作,则选择vector;
    b、若已经知道需要存储元素的数目, 则选择vector;
    c、若需要随机插入/删除(不仅仅在两端),则选择list
    d、只有需要在首端进行插入/删除操作的时候,才选择deque,否则都选择vector。
    e、若既需要随机插入/删除,又需要随机访问,则需要在vector与list间做个折中。
    f、当要存储的是大型负责类对象时,list要优于vector;当然这时候也可以用vector来存储指向对象的指针,同样会取得较高的效率,但是指针的维护非常容易出错,因此不推荐使用。

5、capacity V.S size
    a、capacity是容器需要增长之前,能够盛的元素总数;只有连续存储的容器才有capacity的概念(例如vector,deque,string),list不需要capacity。
    b、size是容器当前存储的元素的数目。
    c、vector默认的容量初始值,以及增长规则是依赖于编译器的。

6、用vector存储自定义类对象时,自定义类对象须满足:
    a、有可供调用的无参构造函数(默认的或自定义的);
    b、有可用的拷贝赋值函数(默认的或自定义的)

7、迭代器iterator 
    a、vector与deque的迭代器支持算术运算,list的迭代器只能进行++/--操作,不支持普通的算术运算。

8. pair类型

 
   pair模板类用来绑定两个对象为一个新的对象,该类型在<utility>头文件中定义。pair类型提供的操作如下表:
 
 pair<T1, T2> p1;  创建一个空的pair对象,它的两个元素分别是T1和T2类型,采用值初始化
 pair<T1, T2> p1(v1, v2);

 创建一个pair对象,它的两个元素分别是T1和T2类型,其中first成员初始化为v1,second成员初始化为v2

 make_pair(v1, v2)  以v1和v2值创建一个新的pair对象,其元素类型分别是v1和v2的类型
 p1 < p2  字典次序:如果p1.first<p2.first或者!(p2.first < p1.first)&& p1.second<p2.second,则返回true
 p1 == p2  如果两个pair对象的first和second成员依次相等,则这两个对象相等。
 p.first  返回p中名为first的(公有)数据成员
 p.second  返回p中名为second的(公有)数据成员
关联容器
 
   关联容器共享大部分顺序容器的操作,但不提供front, push_front, back, push_back以及pop_back操作。
   具体而言,有顺序容器中的:前三种构造函数;关系运算;begin, end, rbegin和rend操作;类型别名;swap和赋值操作,但关联容器不提供assign函数;clear和erase函数,但erase函数返回void类型;关于容器大小的操作,但resize函数不能用于关联容器。
 

map类型
   map类型定义在头文件<map>中。map是键-值对的集合,通常看作关联数组:可使用键作为下标来获取一个值。map类定义内部定义的类型有key_type, mapped_type, value_type,如下表所示:
 
 map<K, V>::key_type  在map容器内,用做索引的键的类型
 map<K, V>::mapped_type  在map容器中,键所关联的值的类型
 map<K, V>::value_type  map的值类型:一个pair类型,它的first元素具有
const map<K, V>::key_type类型,而second元素
则为map<K, V>::mapped_type类型
 
注意:map的元素类型为pair类型,且键成员不可修改。其它类型别名与顺序容器一样。
 
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>
 
键类型的约束
 
   在使用关联容器时,它的键不但有一个类型,而且还有一个相关的比较函数。默认情况下,标准库使用键类型定义的 < 操作符来实现键的比较。这个比较函数必须满足:当一个键和自身比较时,结果必定是false;当两个键之间都不存在“小于”关系时,则容器将之视为相同的键。也就是说,map内的元素按键值升序排列。
 
operator[]
 
 A::reference operator[](const Key& key);
 []操作符返回键key所关联的值的引用;如果该键key不存在,则向map对象添加一个新的元素,元素的键为key,所关联的值采用值初始化。(要特别留意这个副作用)
 
注:map下标操作符返回的类型(mapped_type&)与对map迭代器进行解引用获得的类型(value_type)不相同。

   例如:
      map <string, int> wordCount;   // empty map
      word_count["Hello"] = 1;
   上面的代码首先创建一个空的map对象,然后执行下列步骤:
   1)在wordCount中查找键为“Hello”的元素,没有找到;
   2)将一个新的键-值对插入到wordCount中,其中,键为“Hello”,值为0
   3)读取新插入的键-值对的值,并将它的值赋为1。
应用实例,下面的程序用来统计一篇英文文章中单词出现的频率:

#include <iostream>
#include <map>
using namespace std;

int main()
{ 
    map<string, int> wordCount;
    string word;
    while (cin >> word)
        ++wordCount[word];
    
    for (map<string, int>::iterator it = wordCount.begin(); it != wordCount.end();++it)
        cout<<"Word: "<<(*it).first<<" \tCount: "<<(*it).second<<endl;
    
    return 0;
}

 
map::insert
 
 m.insert(e)    e是一个用在m上的value_type类型的值,如果键(e.first)不在m中,则插入e到m中;如果键已经在m中存在,则保持m不变。
   该函数返回一个pair类型对象,如果发生了插入动作,则返回pair(it, true);否则返回pair(it, false)。其中,it是指向键为e.first那个元素的迭代器。
 m.insert(beg, end)

 beg和end是标记元素范围的迭代器,其中的元素必须为value_type类型的键-值对。对于该范围内的所有元素,如果它的键在m中不存在,则将该键及其关联的值插入到m。返回void类型。

 m.insert(iter, e)  insert(e),并以iter为起点搜索新元素的位置。返回一个迭代器,指向m中键为e.first的元素。
 
注:当需要插入一个map元素时,一是可以用map::value_type来构造一个pair对象,另外,也可以用make_pair来构造这个对象。
 
查找元素
 
 m.count(k)  返回m中k的出现次数(0或1)
 m.find(k)  如果容器中存在键为k的元素,则返回指向该元素的迭代器。
 如果不存在,则返回end()值。
 
删除元素
 
 m.erase(k)  删除m中键为k的元素,返回size_type类型的值,表示删除的元素个数(0或1)
 m.erase(p)  从m中删除迭代器p所指向的元素。p必须指向m中确实存在的元素,而且不能等于e.end()。返回void类型
 m.erase(b, e)  从m中删除[b, e)范围内的元素,返回void类型
set类型
   set类型定义于<set>头文件中。set容器支持大部分map容器的操作,如:构造函数;insert操作;count和find操作;erase操作。两个例外情况是:set不支持下标操作符,而且没有定义mapped_type类型。与map一样,set容器存储的键也必须是唯一的,而且不能修改。
 

multimap和multiset类型
   map和set容器中,一个键只能对应一个实例。而multiset和multimap类型则允许一个键对应多个实例。
 
   multimap和multiset所支持的操作分别与map和set的操作相同,只有一个例外:multimap不支持下标运算。为了顺序一个键可以对应多个值这一特性,map和mulitmap,或set和multiset中相同的操作都以不同的方式做出了一定的修改。
 
元素的添加和删除
 
   map和set容器中的insert和erase操作同样适用于multimap和multiset容器,实现元素的添加和删除。
 
   由于键不要求是唯一的,因此每次调用insert总会添加一个元素。
 
   而带有一个键参数的erase将删除拥有该键的所有元素,并返回删除元素的个数;而带有一个或一对迭代器参数的erase版本只删除指定的元素,并返回void类型。
 
查找元素
 
   在map和set容器中,元素是有序存储的(升序),同样multimap和multiset也一样。因此,在multimap和multiset容器中,如果某个键对应多个实例,则这些实例在容器中将相邻存放,即迭代遍历时,可保证依次返回特定键所关联的所有元素。
 
   要查找特定键所有相关联的值,可以有下面三种方法:
   1)配合使用find和count来查找:count函数求出某键出现的次数,而find操作返回指向第一个键的实例的迭代器。
 
   2)使用lower_bound和upper_bound函数:这两个函数常用于multimap和multiset,但也可以用于map和set容器。所有这些操作都需要传递一个键,并返回一个迭代器。
 m.lower_bound(k)  返回一个迭代器,指向键不小于k的第一个元素
 m.upper_bound(k)  返回一个迭代器,指向键大于k的第一个元素
 m.equal_range(k)  返回一个迭代器的pair对象;它的first成员等价于
 m.lower_bound(k),而second成员则等价于
 m.upper_bound(k)
 
注意:形成的有效区间是[lower_bound(k), upper_bound(i)),是个半开半闭区间。
     lower_bound返回的迭代器不一定指向拥有特定键的元素。如果该键不在容器中,则lower_bound返回在保持容器元素顺序的前提下该键应被插入的第一个位置。
     若键不存在,返回的迭代器相同。
   3)使用equal_range,其实质跟法2)相同。
 
//vector例子
#include <iostream>
using namespace std;
#include
<vector>

int main()
{
vector
<int> vi;
cout
<< "Input scores,end by -1:";
int s;
int m=0;
for(;;){
cin
>> s;
if(s==-1) break;
vi.push_back(s);
if(s>m)
m
= s;
}
int inc = 100-m;
for(int i=0; i<vi.size(); i++)
vi[i]
+= inc;
for(int i=0; i<vi.size(); i++)
cout
<< vi[i] << ' ';
cout
<< endl;
try{
cout
<< "vi[1000]=" << vi[1000] << endl;
cout
<< "vi.at(1000)=" << vi.at(1000) << endl;
}
catch(exception& e){
cout
<< e.what() << endl;
}
}

存取首尾元素

front()与back()操作,取后一个和最前一个元素,注意其返回是引用,其而是左值(l_value),因此可以赋值. 做类似于vecobj.front() = 3;的操作,但要保证front空间有效,否则形为无法预测。 

使用assign

assign可以改变大小和初值,大小是随意的,不受开始时大小的限制,如果设置为0,则清空。

assign(5,0)把vector改为5个大小,并用0添充

assign(iax+3,iax+5); 从数组第4到5个填充,注意左闭右开,即可取到iax[3]与iax[4]

使用insert

insert(it, x),在it前插入一个元素x

insert(it,first,last),在it前插入一个序列[first,last)左闭右开

使用erase

erase(it)删除在it处的元素,返回值为下一元素。如果intVec.erase(intVec.end());并不会报错,如果删除一个序列[first,last),使用erase(first,last)

//list.cc
#include <iostream>
using namespace std;
#include
<list>

int main()
{
int cpp[5] = {3,5,6,8,9};
int java[8] = {1,2,3,4,5,6,7,8};
int Unix[4] = {3,4,5,6};
list
<int> li;
li.insert(li.begin(),cpp,cpp
+5);
li.insert(li.begin(),java,java
+8);
li.insert(li.begin(),unix,unix
+4);
li.sort();
//排序
li.unique();//删除相邻的重复的元素,只留一份
list<int>::iterator it = li.begin();
while(it!=li.end())
cout
<< *it++ << ' ';
cout
<< endl;
}

  二、操作

1.resize & clear

使用resize(n)改变大小,使用resize(n, val)如果需要用T(val) 来填满空闲值。

2.front ()& back()

如果listObj非常量对象,返回是一个左值函数

3.插入操作

insert(iterator it , val)

insert(iterator it, first, last)

insert(iteratot it, n, x)//插入n个x

4.移除 [按值删]

remove(x); //vector.erase(integrator it)

//map.cc
#include <iostream>
#include
<map>
using namespace std;
#include
<string>

int main()
{
map
<int, string> m;
m.insert(make_pair(
1001,"李明"));
m.insert(make_pair(
1002,"王国"));
m.insert(make_pair(
1005,"失小"));
m[
1006] = "刘华";
m.insert(make_pair(
1001,"娜娜"));
map::iterator it;
it
= m.begin();
while(it!=it.end()){
cout
<< it->first << ':' << it->second << endl;
++ it;//会比it++性能高
}
}

对于set,map来说重复的插入被忽略.

//multimap
//一切操作都是对key而言的
//key一定要支持小于运算符,不然是不能进行排序的
#include <iostream>
using namespace std;
#include
<map>
#include
<string>

int main()
{
typedef multimap
<string,string> M;
M mss;
M::iterator ib,ie;
mss.insert(make_pair(
"a","b"));
mss.insert(make_pair(
"c","d"));
mss.insert(make_pair(
"e","f"));
mss.insert(make_pair(
"c","p"));
mss.insert(make_pair(
"a","m"));
mss.insert(make_pair(
"c","d"));
mss.insert(make_pair(
"a","p"));
ib
= mss.begin();
ie
= mss.end();
while(ib!=ie){
cout
<< ib->first <<',' << ib->second << endl;
++ ib;
}
//end while
cout << "a count : " << mss.count("a") << endl;
cout
<< "c count : " << mss.count("c") << endl;
ib
= mss.lower_bound("c");
ie
= mss.upper_bound("c");
while(ib!=ie){
cout
<< ib->first <<',' << ib->second << endl;
++ ib;
}
//end while

}
//end main

//set
//同样的插入同样被忽略
#include <iostream>
using namespace std;
#include
<set>

int main()
{
set<int> si;
int userid[5] = {1,3,3,4,5};
for(int i=0;i<5; i++)
si.insert(userid[i]);
set<int>::iterator it;
it
= si.begin();
while(it!=si.end())
cout
<< *it++ << ' ';
cout
<< endl;
cout
<< "user 3 : " << (si.find(3)!=si.end()) << endl;
cout
<< "user 9 : " << (si.find(9)!=si.end()) << endl;
}

//multiset
//用的是平衡二叉树,所以它的查找效率是相当高的.
#include <iostream>
using namespace std;
#include
<set>

template
<typename Iter>
void show(Iter ib, Iter ie)
{
while(ib!=ie)
cout
<< *ib++ << ' ';
cout
<< endl;
}
int main()
{
int a[5] = {5,1,7,5,1};
multiset
<int> pids(a,a+5);
show(pids.begin(),pids.end());
pids.insert(
7);
pids.insert(
7);
pids.insert(
7);
pids.erase(pids.find(
5));
show(pids.begin(),pids.end());
cout
<< "end process 7..." << endl;
pids.erase(
7);
show(pids.begin(),pids.end());
}
//end main

  

 


原文地址:https://www.cnblogs.com/xiaoxxy/p/2163378.html