STL 提供三种类型的组件:容器、迭代器和算法,它们都支持泛型程序设计标准。
容器主要有两类:顺序容器和关联容器。
顺序容器(vector、 list、 deque 和 string等)是一系列元素的有序集合。
关联容器(set、 multiset、 map 和 multimap)包含查找元素的键值。
迭代器的作用是遍历容器。
STL 算法库包含四类算法:排序算法、不可变序算法、变序性算法和数值算法。
vector 向量容器不但能像数组一样对元素进行随机访问,还能在尾部插入元素,是一
种简单、高效的容器,完全可以代替数组。
vector 具有内存自动管理的功能,对于元素的插入和删除,可动态调整所占的内存空间。
vector 容器的下标是从 0 开始计数的,也就是说,如果 vector 容器的大小是 n,那么,元素的下标是 0~n-1。对于 vector 容器的容量定义,可以事先定义一个固定大小,事后,可以随时调整其大小;也可以事先不定义,随时使用 push_back()方法从尾部扩张元素,也可以使用 insert()在某个元素位置前插入新元素。
vector 容器有两个重要的方法, begin()和 end()。 begin()返回的是首元素位置的迭代器;end()返回的是最后一个元素的下一元素位置的迭代器。
#include
using namespace std;
创建 vector 对象常用的有三种形式。
(1)不指定容器的元素个数,如定义一个用来存储整型的容器:
vector<int> v;
(2)创建时,指定容器的大小,如定义一个用来存储 10 个 double 类型元素的向量容器:
vector<double> v(10);
注意,元素的下标为 0~9;另外,每个元素的值被初始化为 0.0。
(3)创建一个具有 n 个元素的向量容器对象,每个元素具有指定的初始值:
vector<double> v(10, 8.6);
上述语句定义了 v 向量容器,共有 10 个元素,每个元素的值是 8.6。
通常使用 push_back()对 vector 容器在尾部追加新元素。尾部追加元素, vector 容器会自动分配新内存空间。可对空的 vector 对象扩张,也可对已有元素的 vector 对象扩张。
vector<int> v;
v.push_back(33);
访问或遍历 vector 对象是常要做的事情。对于 vector 对象,可以采用下标方式随意访
问它的某个元素,当然,也可以以下标方式对某元素重新赋值,这点类似于数组的访问方式。
vector<int> v(2);
v[0]=2;
v[1]=7;
常使用迭代器配合循环语句来对 vector 对象进行遍历访问,迭代器的类型一定要与它
要遍历的 vector 对象的元素类型一致。
vector<int>::iterator it;
for(it=v.begin();it!=v.end();it++)
{
//输出迭代器上的元素值
cout<<*it<<" ";
}
insert()方法可以在 vector 对象的任意位置前插入一个新的元素,同时, vector 自动扩
张一个元素空间,插入位置后的所有元素依次向后挪动一个位置。
要注意的是, insert()方法要求插入的位置,是元素的迭代器位置,而不是元素的下标。
//在最前面插入新元素,元素值为 8
v.insert(v.begin(),8);
//在第 2 个元素前插入新元素 1
v.insert(v.begin()+2,1);
//在向量末尾追加新元素 3
v.insert(v.end(),3);
erase()方法可以删除 vector 中迭代器所指的一个元素或一段区间中的所有元素。
clear()方法则一次性删除 vector 中的所有元素。
//删除 2 个元素,从 0 开始计数
v.erase(v.begin()+2);
//清空向量
v.clear();
reverse 反向排列算法,需要定义头文件“#include
reverse 算法可将向量中某段迭代器区间元素反向排列.
//反向排列向量的从首到尾间的元素
reverse(v.begin(),v.end());
使用 sort 算法,需要声明头文件“#include
sort 算法要求使用随机访问迭代器进行排序,在默认的情况下,对向量元素进行升序
排列
//排序,升序排列
sort(v.begin(),v.end());
//自己设计排序比较函数:对元素的值进行降序排列
bool Comp(const int &a,const int &b)
{
if(a!=b)return a>b;
else return a>b;
}
//按 Comp 函数比较规则排序
sort(v.begin(),v.end(),Comp);
使用 size()方法可以返回向量的大小,即元素的个数。
使用 empty()方法返回向量是否为空。
//向量的大小
v.size();
//向量是否为空,如果非空,则返回逻辑假,即 0,否则返回逻辑真,即 1
v.empty();
C++STL 提供了 string 基本字符系列容器来处理字符串, 可以把 string理解为字符串类,它提供了添加、删除、替换、查找和比较等丰富的方法。
使用 string 容器,需要头文件包含声明“#include
创建了字符串对象 s, s 是一个空字符串,其长度为 0:
string s;
s.length();
给 string 对象赋值一般有两种方式。
(1)直接给字符串对象赋值
string s;
s="hello,C++STL.";
(2)更常用的方法是,把字符指针赋给一个字符串对象
string s;
char ss[5000];
//scanf 的输入速度比 cin 快得多
//scanf 是 C 语言的函数,不支持 string 对象
scanf("%s",&ss);
//把整个字符数组赋值给 string 对象
s=ss;
在 string 对象的尾部添加一个字符(char),采用“+”操作符即可.
string s;
s=s+'a';
s=s+'b';
s=s+'c';
//s="abc";
从尾部追加的方式有两种。
(1)直接采用“+”操作符
string s;
s=s+"abc";
s=s+"123";
//s="abc123"
(2)采用 append()方法
string s;
s.append("abc");
s.append("123");
//s="abc123"
可以使用 insert()方法把一个字符插入到迭代器位置之前
string s;
s="123456";
//定义迭代器
string::iterator it;
//迭代器位置为字符串首
it=s.begin();
//把字符'p'插入到第 1 个字符前(注意,字符位置是从 0 开始计数)
s.insert(it+1,'p');
//s=1p23456
一般使用下标方式随机访问 string 对象的元素,下标是从 0 开始计数的。另外, string
对象的元素是一个字符(char)
string s;
s="abc123456";
//输出 string 对象的首元素a
cout<0]<//两个相同的字符相减值为 0
cout<0]-'a'<
(1)清空一个字符串,则直接给它赋空字符串即可。
(2)使用 erase()方法删除迭代器所指的那个元素或一个区间中的所有元素。
string s;
s="abc123456";
//定义迭代器变量,指向字符串对象首元素
string::iterator it=s.begin();
//删除第 3 个元素,元素位置从 0 开始计数
s.erase(it+3);
//删除 0~4 区间的所有元素
s.erase(it,it+4);
//清空字符串
s="";
采用 length()方法可返回字符串的长度;采用 empty()方法,可返回字符串是否为空,
如果字符串为空,则返回逻辑真,即 1,否则,返回逻辑假,即 0。
//字符串长度
s.length();
//判断字符串是否为空
s.empty();
使用 replace()方法可以很方便地替换 string 对象中的字符, replace()方法的重载函数相
当多,常用的只有一两个
string s;
s="abc123456";
//从第 3 个开始,将连续的 3 个字符替换为“good”
//即将“abc”替换为“good”
s.replace(3,3,"good");
//s=abcgood456
采用 find()方法可查找字符串中的第一个字符元素(char, 用单引号界定)或者子串(用双引号界定), 如果查到, 则返回下标值(从 0 开始计数), 如果查不到, 则返回 4294967295。
string s;
s="cat dog cat";
//查找第一个字符‘c’,返回下标值
cout<'c')<//查找第一个子串“dog”,返回下标值
cout<"dog")<//查找第一个子串“dogc”,查不到则返回 4294967295
cout<"dogc")<
string 对象可与使用 compare()方法与其他字符串相比较。如果它比对方大,则返回 1;如果它比对方小,则返回-1;如果它与对方相同(相等),则返回 0。
string s;
s="cat dog cat";
//s 比“cat”字符串大,返回 1
cout<"cat")<//s 与“cat dog cat”相等,返回 0
cout<"cat dog cat")<//s 比“dog”小,返回-1
cout<"dog")<
采用 reverse()方法可将 string 对象迭代器所指向的一段区间中的元素(字符)反向排
序。 reverse()方法需要声明头文件“#include
string s;
s="123456789";
reverse(s.begin(),s.end());
//s=987654321
string 对象可以作为 vector 向量的元素,这种用法,类似于字符串数组。
vector<string> v;
v.push_back("Jack");
v.push_back("Mike");
v.push_back("Tom");
cout<0]<//Jack
cout<1]<//Mike
cout<2]<//Tom
cout<0][0]<//J
cout<1][0]<//M
cout<2].length()<//3
将读入的数据当成字符串来处理
string s;
s="1234059";
int i;
int sum=0;
for(i=0;iif(s[i]=='0')sum+=0;
else if(s[i]=='1')sum+=1;
else if(s[i]=='2')sum+=2;
else if(s[i]=='3')sum+=3;
else if(s[i]=='4')sum+=4;
else if(s[i]=='5')sum+=5;
else if(s[i]=='6')sum+=6;
else if(s[i]=='7')sum+=7;
else if(s[i]=='8')sum+=8;
else if(s[i]=='9')sum+=9;
}
cout<//24
字符数组与 string 对象的输入与输出
string s;
char ss[100];
//输入字符串到字符数组中
scanf("%s",&ss);
//字符数组赋值线字符串对象
s=ss;
//用 printf 输出字符串对象,要采用 c_str()方法
printf(s.c_str());
//用 printf 输出字符数组
printf("%s",ss);
//用 cout 输出字符串对象
cout<//用 cout 输出字符数组
cout<
在 C 语言中, sscanf 函数很管用,它可以把一个字符串按你需要的方式分离出子串,
甚至是数字。
string s1,s2,s3;
char sa[100],sb[100],sc[100];
//将字符串分离成子串,分隔符为空格
sscanf("abc 123 pc","%s %s %s",sa,sb,sc);
s1=sa;
s2=sb;
s3=sc;
cout<" "<" "<//将字符串分离成数字,分隔符为空格
//当用到数字的时候,跟 scanf 一样,它要传指针地址
int a,b,c;
sscanf("1 2 3","%d %d %d",&a,&b,&c);
cout<" "<" "<//将字符串分离成数字,分隔符为“,”和“$”
//当用到数字的时候,跟 scanf 一样,它要传指针地址
int x,y,z;
sscanf("4,5$6","%d,%d$%d",&x,&y,&z);
cout<" "<" "<//abc 123 pc
//1 2 3
//4 5 6
有时候, string 对象与数值之间需要相互转换
#include
#include
#include
using namespace std;
//C++方法:将数值转换为 string
string convertToString(double x)
{
ostringstream o;
if (o << x)
return o.str();
return "conversion error";//if error
}
//C++方法:将 string 转换为数值
double convertFromString(const string &s)
{
istringstream i(s);
double x;
if (i >> x)
return x;
return 0.0;//if error
}
//将数值转换为 string 的第一种方法: C 方法
char b[10];
string a;
sprintf(b,"%d",1975);
a=b;
cout<//将数值转换为 string 的第二种方法: C++方法
string cc=convertToString(1976);
cout<//将 string 转换为数值的方法: C++方法
string dd="2006";
int p=convertFromString(dd)+2;
cout<
set 集合容器实现了红黑树(Red-Black Tree)的平衡二叉检索树的数据结构,在插入
元素时,它会自动调整二叉树的排列,把该元素放到适当的位置,以确保每个子树根节点的键值大于左子树所有节点的键值,而小于右子树所有节点的键值;另外,还得确保根节点左子树的高度与右子树的高度相等,这样,二叉树的高度最小,从而检索速度最快。要注意的是,它不会重复插入相同键值的元素,而采取忽略处理。
平衡二叉检索树的检索使用中序遍历算法, 检索效率高于 vector、 deque 和 list 等容器。
另外,采用中序遍历算法可将键值由小到大遍历出来,所以,可以理解为平衡二叉检索树在插入元素时,就会自动将元素按键值由小到大的顺序排列。
对于 set 容器中的键值,不可直接去修改。因为如果把容器中的一个键值修改了, set
容器会根据新的键值旋转子树,以保持新的平衡,这样,修改的键值很可能就不在原先那个位置上了。换句话来说,构造 set 集合的主要目的就是为了快速检索。
multiset(多重集合容器)、 map(映照容器)和 multimap(多重映照容器)的内部结构也是平衡二叉检索树。
使用 set 前,需要在程序的头文件中包含声明“#include
创建 set 对象时,需要指定元素的类型,这一点与其他容器一样。
//定义元素类型为 int 的集合对象 s,当前没有任何元素
//元素的排列采用默认的比较规则,当然,可以自定义比较规则函数
set<int> s;
采用 insert()方法把元素插入集合中去,插入的具体规则在默认的比较规则下,是按元
素值由小到大插入,如果自己指定了比较规则函数,则按自定义比较规则函数插入。
使用前向迭代器对集合中序遍历,其结果正好是元素排序的结果。
//定义元素类型为 int 的集合对象 s,当前没有任何元素
set<int> s;
//插入了 5 个元素,但由于 8 有重复,第二次插入的 8 并没有执行
s.insert(8);//第一次插入 8,可以插入
s.insert(1);
s.insert(12);
s.insert(6);
s.insert(8);//第二次插入 8,重复元素,不会插入
//中序遍历集合中的元素
set<int>::iterator it;//定义前向迭代器
//中序遍历集合中的所有元素
for(it=s.begin();it!=s.end();it++)
{
cout<<*it<<" ";
}
//1 6 8 12
使用反向迭代器 reverse_iterator 可以反向遍历集合,输出的结果正好是集合元素的反
向排序结果。它需要用到 rbegin()和 rend()两个方法,它们分别给出了反向遍历的开始位置和结束位置。
set<int>::reverse_iterator rit;//定义反向迭代器
for(rit=s.rbegin();rit!=s.rend();rit++)
{
cout<<*rit<<" ";
}
与插入元素的处理一样,集合具有高效的删除处理功能,并自动重新调整内部的红黑
树的平衡。
删除的对象可以是某个迭代器位置上的元素、等于某键值的元素、一个区间上的元素
和清空集合。
//删除键值为 6 的那个元素
s.erase(6);
//清空集合
s.clear();
使用 find()方法对集合进行搜索,如果找到查找的键值,则返回该键值的迭代器位置,
否则,返回集合最后一个元素后面的一个位置,即 end()。
set<int>::iterator it;//定义前向迭代器
//查找键值为 6 的元素
it=s.find(6);
if(it!=s.end())//找到
cout<<*it<else//没找到
cout<<"not find it"<//查找键值为 20 的元素
it=s.find(20);
if(it!=s.end())//找到
cout<<*it<else//没找到
cout<<"not find it"<//6
//not find it
使用 insert()将元素插入到集合中去的时候,集合会根据设定的比较函数将该元素放到
该放的节点上去。在定义集合的时候,如果没有指定比较函数,那么采用默认的比较函数,即按键值由小到大的顺序插入元素。在很多情况下,需要自己编写比较函数。
//自定义比较函数 myComp,重载“()”操作符
struct myComp
{
bool operator()(const int &a,const int &b)
{
if(a!=b)
return a>b;
else
return a>b;
}
};
//定义元素类型为 int 的集合对象 s,当前没有任何元素
//采用的比较函数是 myComp
set<int,myComp> s;
//插入了 5 个元素,但由于 8 有重复,所以第二次插入的 8 并没有执行
s.insert(8);//第一次插入 8,可以插入
s.insert(1);
s.insert(12);
s.insert(6);
s.insert(8);//第二次插入 8,重复元素,不会插入
set<int,myComp>::iterator it;//定义前向迭代器
for(it=s.begin();it!=s.end();it++)
{
cout<<*it<<" ";
}
//12 8 6 1
(2)如果元素是结构体,那么,可以直接把比较函数写在结构体内。
struct Info
{
string name;
float score;
//重载“<”操作符,自定义排序规则
bool operator < (const Info &a) const
{
//按 score 由大到小排列。如果要由小到大排列,使用“>”号即可。
return a.score//定义元素类型为 Info 结构体的集合对象 s,当前没有任何元素
set s;
//定义 Info 类型的元素
Info info;
//插入 3 个元素
info.name="Jack";
info.score=80.5;
s.insert(info);
info.name="Tomi";
info.score=20.5;
s.insert(info);
info.name="Nacy";
info.score=60.5;
s.insert(info);
set ::iterator it;//定义前向迭代器
for(it=s.begin();it!=s.end();it++)
{
cout<<(*it).name<<" : "<<(*it).score<//Jack : 80.5
//Nacy : 60.5
//Tomi : 20.5
multiset 与 set 一样,也是使用红黑树来组织元素数据的,唯一不同的是, multiset 允
许重复的元素键值插入,而 set 则不允许。
multiset 也需声明头文件包含“#include
插入了重复键值“123”,最后中序遍历了 multiset 对象。
//定义元素类型为 string 的多重集合对象 s,当前没有任何元素
multiset<string> ms;
ms.insert("abc");
ms.insert("123");
ms.insert("111");
ms.insert("aaa");
ms.insert("123");
multiset<string>::iterator it;
for(it=ms.begin();it!=ms.end();it++)
{
cout<<*it<//111
//123
//123
//aaa
//abc
采用 erase()方法可以删除 multiset 对象中的某个迭代器位置上的元素、某段迭代器区
间中的元素、键值等于某个值的所有重复元素,并返回删除元素的个数。采用 clear()方法可以清空元素。
//定义元素类型为 string 的多重集合对象 s,当前没有任何元素
multiset<string> ms;
ms.insert("abc");
ms.insert("123");
ms.insert("111");
ms.insert("aaa");
ms.insert("123");
multiset<string>::iterator it;
for(it=ms.begin();it!=ms.end();it++)
{
cout<<*it<//删除值为“123”的所有重复元素,返回删除元素总数 2
int n=ms.erase("123");
cout<<"Total deleted : "<//输出删除后的剩余元素
cout<<"all elements after deleted :"<for(it=ms.begin();it!=ms.end();it++)
{
cout<<*it</**
111
123
123
aaa
abc
Total deleted : 2
all elements after deleted :
111
aaa
abc
**/
使用 find()方法查找元素,如果找到,则返回该元素的迭代器位置(如果该元素存在
重复,则返回第一个元素重复元素的迭代器位置);如果没有找到,则返回 end()迭代
器位置。
//定义元素类型为 string 的多重集合对象 s,当前没有任何元素
multiset<string> ms;
ms.insert("abc");
ms.insert("123");
ms.insert("111");
ms.insert("aaa");
ms.insert("123");
multiset<string>::iterator it;
//查找键值“123”
it=ms.find("123");
if(it!=ms.end())//找到
{
cout<<*it<else//没有找到
{
cout<<"not find it"<"bbb");
if(it!=ms.end())//找到
{
cout<<*it<else//没有找到
{
cout<<"not find it"<//123
//not find it
map 映照容器的元素数据是由一个键值和一个映照数据组成的,键值与映照数据之间
具有一一映照的关系。
map 映照容器的数据结构也是采用红黑树来实现的,插入元素的键值不允许重复,比
较函数只对元素的键值进行比较,元素的各项数据可通过键值检索出来。由于 map 与 set采用的都是红黑树的数据结构
使用 map 容器需要头文件包含语句“#include ,map 文件在 C:\ProgramFiles\Microsoft Visual Studio\VC98\Include 文件夹内。map 文件也包含了对 multimap 多重映照容器的定义。
创建 map 对象,键值与映照数据的类型由自己定义。在没有指定比较函数时,元素的
插入位置是按键值由小到大插入到黑白树中去的,这点和 set 一样。
//定义 map 对象,当前没有任何元素
map<string,float> m;
//插入元素,按键值的由小到大放入黑白树中
m["Jack"]=98.5;
m["Bomi"]=96.0;
m["Kate"]=97.5;
//前向遍历元素
map<string,float>::iterator it;
for(it=m.begin();it!=m.end();it++)
{
//输出键值与映照数据
cout<<(*it).first<<" : "<<(*it).second</**
Bomi : 96
Jack : 98.5
Kate : 97.5
**/
程序编译时,会产生代号为“warning C4786”的警告,“4786”是标记符超长警告的
代号。可以在程序的头文件包含代码的前面使用“#pragma warning(disable:4786)”宏语句,强制编译器忽略该警告。4786 号警告对程序的正确性和运行并无影响。
与 set 容器一样,map 映照容器的 erase()删除元素函数,可以删除某个迭代器位置上
的元素、等于某个键值的元素、一个迭代器区间上的所有元素,当然,也可使用 clear()方法清空 map 映照容器。
//删除键值为 28 的元素
m.erase(28);
可以使用反向迭代器 reverse_iterator 反向遍历 map 照映容器中的数据,它需要 rbegin()方法和 rend()方法指出反向遍历的起始位置和终止位置。
//反向遍历元素
map<int,char>::reverse_iterator rit;
for(rit=m.rbegin();rit!=m.rend();rit++)
{
//输出键值与映照数据
cout<<(*rit).first<<" : "<<(*rit).second<
使用 find()方法来搜索某个键值,如果搜索到了,则返回该键值所在的迭代器位置,
否则,返回 end()迭代器位置。由于 map 采用黑白树数据结构来实现,所以搜索速度是极快的。
map<int,char>::iterator it;
it=m.find(28);
if(it!=m.end())//搜索到该键值
{
cout<<(*it).first<<" : "<<(*it).second<else
{
cout<<"not found it"<//28 : k
将元素插入到 map 中去的时候,map 会根据设定的比较函数将该元素放到该放的节点
上去。在定义 map 的时候,如果没有指定比较函数,那么采用默认的比较函数,即按键值由小到大的顺序插入元素。在很多情况下,需要自己编写比较函数。
编写比较函数与 set 比较函数是一致的,因为它们的内部数据结构都是红黑树。
(1)如果元素不是结构体,那么,可以编写比较函数。下面这个程序编写的比较规则
是要求按键值由大到小的顺序将元素插入到 map 中:
//自定义比较函数 myComp
struct myComp
{
bool operator()(const int &a,const int &b)
{
if(a!=b)return a>b;
else
return a>b;
}
};
//定义 map 对象,当前没有任何元素
map<int,char,myComp> m;
//插入元素,按键值的由小到大放入黑白树中
m[25]='m';
m[28]='k';
m[10]='x';
m[30]='a';
//使用前向迭代器中序遍历 map
map<int,char,myComp>::iterator it;
for(it=m.begin();it!=m.end();it++)
{
cout<<(*it).first<<" : "<<(*it).second</***
30 : a
28 : k
25 : m
10 : x
**/
(2)如果元素是结构体,那么,可以直接把比较函数写在结构体内。
struct Info
{
string name;
float score;
//重载“<”操作符,自定义排序规则
bool operator < (const Info &a) const
{
//按 score 由大到小排列。如果要由小到大排列,使用“>”号即可
return a.score//定义 map 对象,当前没有任何元素
mapint > m;
//定义 Info 结构体变量
Info info;
//插入元素,按键值的由小到大放入黑白树中
info.name="Jack";
info.score=60;
m[info]=25;
info.name="Bomi";
info.score=80;
m[info]=10;
info.name="Peti";
info.score=66.5;
m[info]=30;
//使用前向迭代器中序遍历 map
mapint >::iterator it;
for(it=m.begin();it!=m.end();it++)
{
cout<<(*it).second<<" : ";
cout<<((*it).first).name<<" "<<((*it).first).score</**
10 : Bomi 80
30 : Peti 66.5
25 : Jack 60
**/
对数字的各位进行分离,采用取余等数学方法操作是很耗时的。而把数字当成字符串,使用 map 的映照功能,很方便地实现了数字分离。
//定义 map 对象,当前没有任何元素
map<char,int> m;
//赋值:字符映射数字
m['0']=0;
m['1']=1;
m['2']=2;
m['3']=3;
m['4']=4;
m['5']=5;
m['6']=6;
m['7']=7;
m['8']=8;
m['9']=9;
/*上面的 10 条赋值语句可采用下面这个循环来简化代码编写
for(int j=0;j<10;j++)
{
m['0'+j]=j;
}
*/
string sa,sb;
sa="6234";
int i;
int sum=0;
for(i=0;icout<<"sum = "<//sum = 15
在很多情况下,需要实现将数字映射为相应的字符
//定义 map 对象,当前没有任何元素
map<int,char> m;
//赋值:字符映射数字
m[0]='0';
m[1]='1';
m[2]='2';
m[3]='3';
m[4]='4';
m[5]='5';
m[6]='6';
m[7]='7';
m[8]='8';
m[9]='9';
/*上面的 10 条赋值语句可采用下面这个循环来简化代码编写
for(int j=0;j<10;j++)
{
m[j]='0'+j;
}
*/
int n=7;
string s="The number is ";
cout<//The number is 7
multimap 与 map 基本相同,唯独不同的是,multimap 允许插入重复键值的元素。由于允许重复键值存在,所以,multimap 的元素插入、删除、查找都与 map 不相同。
要使用 multimap,则需要头文件包含语句“#include 。map 文件在 C:\ProgramFiles\Microsoft Visual Studio\VC98\Include 文件夹中。
可以重复插入元素,插入元素需要使用 insert()方法。
//定义 map 对象,当前没有任何元素
multimap<string,double> m;
//插入元素
m.insert(pair<string,double>("Jack",300.5));
m.insert(pair<string,double>("Kity",200));
m.insert(pair<string,double>("Memi",500));
//重复插入键值“Jack”
m.insert(pair<string,double>("Jack",306));
//使用前向迭代器中序遍历 multimap
multimap<string,double>::iterator it;
for(it=m.begin();it!=m.end();it++)
{
cout<<(*it).first<<" : "<<(*it).second</**
Jack : 300.5
Jack : 306
Kity : 200
Memi : 500
**/
删除操作采用 erase()方法,可删除某个迭代器位置上的元素、等于某个键值的所有重
复元素、一个迭代器区间上的元素。使用 clear()方法可将 multimap 容器中的元素清空。
因为有重复的键值,所以,删除操作会将要删除的键值一次性从 multimap 中删除。
//删除键值等于“Jack”的元素
m.erase("Jack");
由于 multimap 存在重复的键值,所以 find()方法只返回重复键值中的第一个元素的迭
代器位置,如果没有找到该键值,则返回 end()迭代器位置。
it=m.find("Jack");
if(it!=m.end())//找到
{
cout<<(*it).first<<" "<<(*it).second<else//没找到
{
cout<<"not find it"<"Nacy");
if(it!=m.end())//找到
{
cout<<(*it).first<<" "<<(*it).second<else//没找到
{
cout<<"not find it"<//Jack 300.5
//not find it
deque 双端队列容器与 vector 一样,采用线性表顺序存储结构。但与 vector 唯一不同
的是,deque 采用分块的线性存储结构来存储数据,每块的大小一般为 512 字节,称为一个 deque 块,所有的 deque 块使用一个 Map 块进行管理,每个 Map 数据项记录各个 deque块的首地址。这样一来,deque 块在头部和尾部都可插入和删除元素,而不需移动其他元素(使用 push_back()方法在尾部插入元素,会扩张队列;而使用 push_front()方法在首部插入元素和使用 insert()方法在中间插入元素,只是将原位置上的元素值覆盖,不会增加新元素)。一般来说,当考虑到容器元素的内存分配策略和操作的性能时,deque 相对于 vector更有优势。
使用 deque 需要声明头文件包含“#include
创建 deque 对象的方法通常有三种。
(1)创建没有任何元素的 deque 对象,如:
deque<int> d;
deque<float> dd;
(2)创建具有 n 个元素的 deque 对象,如:
deque<int> d(10);//创建具有 10 个整型元素的 deque 对象 d
(3)创建具有 n 个元素的 deque 对象,并赋初值,如:
deque<int> d(10,8.5);
//创建具有 10 个整型元素的 deque 对象 d,每个元素值为 8.5
(1)使用 push_back()方法从尾部插入元素,会不断扩张队列。
//定义 deque 对象,元素类型是整型
deque<int> d;
//从尾部连续插入三个元素
d.push_back(1);
d.push_back(2);
d.push_back(3);
//以数组方式输出元素
cout<0]<<" "<1]<<" "<2]<//1 2 3
(2)从头部插入元素,不会增加新元素,只将原有的元素覆盖。
//定义 deque 对象,元素类型是整型
deque<int> d;
//从尾部连续插入三个元素
d.push_back(1);
d.push_back(2);
d.push_back(3);
//从头部插入元素,不会增加新元素,只将原有的元素覆盖
d.push_front(10);
d.push_front(20);
//以数组方式输出元素
cout<0]<<" "<1]<<" "<2]<<" "<3]<<" "<4]<//20 10 1 2 3
(3)从中间插入元素,不会增加新元素,只将原有的元素覆盖。
//定义 deque 对象,元素类型是整型
deque<int> d;
//从尾部连续插入三个元素
d.push_back(1);
d.push_back(2);
d.push_back(3);
//中间插入元素,不会增加新元素,只将原有的元素覆盖
d.insert(d.begin()+1,88);
//以数组方式输出元素
cout<0]<<" "<1]<<" "<2]<//1 88 2
(1)以数组方式遍历。
//定义 deque 对象,元素类型是整型
deque<int> d;
//从尾部连续插入三个元素
d.push_back(1);
d.push_back(2);
d.push_back(3);
//以数组方式输出元素
int i;
for(i=0;icout<" ";
}
//1 2 3
(2)以前向迭代器的方式遍历。
//定义 deque 对象,元素类型是整型
deque<int> d;
//从尾部连续插入三个元素
d.push_back(1);
d.push_back(2);
d.push_back(3);
//以前向迭代器的方式遍历
deque<int>::iterator it;
for(it=d.begin();it!=d.end();it++)
{
cout<<*it<<" ";
}
//1 2 3
采用反向迭代器对双端队列容器进行反向遍历。
//定义 deque 对象,元素类型是整型
deque<int> d;
//从尾部连续插入三个元素
d.push_back(1);
d.push_back(2);
d.push_back(3);
//以反向迭代器的方式遍历
deque<int>::reverse_iterator rit;
for(rit=d.rbegin();rit!=d.rend();rit++)
{
cout<<*rit<<" ";
}
//3 2 1
可以从双端队列容器的首部、尾部、中部删除元素,并可以清空双端队列容器。
(1)采用 pop_front()方法从头部删除元素。
//定义 deque 对象,元素类型是整型
deque<int> d;
//从尾部连续插入五个元素
d.push_back(1);
d.push_back(2);
d.push_back(3);
d.push_back(4);
d.push_back(5);
//从头部删除元素
d.pop_front();
d.pop_front();
//以前向迭代器的方式遍历
deque<int>::iterator it;
for(it=d.begin();it!=d.end();it++)
{
cout<<*it<<" ";
}
//3 4 5
(2)采用 pop_back()方法从尾部删除元素。
//定义 deque 对象,元素类型是整型
deque<int> d;
//从尾部连续插入五个元素
d.push_back(1);
d.push_back(2);
d.push_back(3);
d.push_back(4);
d.push_back(5);
//从尾部删除元素
d.pop_back();
//以前向迭代器的方式遍历
deque<int>::iterator it;
for(it=d.begin();it!=d.end();it++)
{
cout<<*it<<" ";
}
//1 2 3 4
(3)使用 erase()方法从中间删除元素,其参数是迭代器位置。
//定义 deque 对象,元素类型是整型
deque<int> d;
//从尾部连续插入五个元素
d.push_back(1);
d.push_back(2);
d.push_back(3);
d.push_back(4);
d.push_back(5);
//从中间删除元素,erase 的参数是迭代器位置
d.erase(d.begin()+1);
//以前向迭代器的方式遍历
deque<int>::iterator it;
for(it=d.begin();it!=d.end();it++)
{
cout<<*it<<" ";
}
// 1 3 4 5
(4)使用 clear()方法清空 deque 对象。
//定义 deque 对象,元素类型是整型
deque<int> d;
//从尾部连续插入五个元素
d.push_back(1);
d.push_back(2);
d.push_back(3);
d.push_back(4);
d.push_back(5);
//清空元素
d.clear();
//输出元素的个数
cout<//0
list 容器实现了双向链表的数据结构,数据元素是通过链表指针串连成逻辑意义上的线性表,这样,对链表的任一位置的元素进行插入、删除和查找都是极快速的。
list 的每个节点有三个域:前驱元素指针域、数据域和后继元素指针域。前驱元素指针域保存了前驱元素的首地址;数据域则是本节点的数据;后继元素指针域则保存了后继元素的首地址。list 的头节点的前驱元素指针域保存的是链表中尾元素的首地址,而 list 的尾节点的后继元素指针域则保存了头节点的首地址,这样,就构成了一个双向循环链。
由于 list 对象的节点并不要求在一段连续的内存中,所以,对于迭代器,只能通过“++”或“- -”的操作将迭代器移动到后继/前驱节点元素处。而不能对迭代器进行+n 或-n 的操作,这点,是与 vector 等不同的地方。
使用 list 需要声明头文件包含“#include ”
,list 文件在 C:\Program Files\MicrosoftVisual Studio\VC98\Include 文件夹中。
(1)创建空链表,如:
list<int> l;
(2)创建具有 n 个元素的链表,如:
list<int> l(10) ; //创建具有 10 个元素的链表
有三种方法往链表里插入新元素:
(1)采用 push_back()方法往尾部插入新元素,链表自动扩张。
(2)采用 push_front()方法往首部插入新元素,链表自动扩张。
(3)采用 insert()方法往迭代器位置处插入新元素,链表自动扩张。注意,迭代器只能进行“++”或“- -”操作,不能进行+n 或-n 的操作,因为元素的位置并不是物理相连的。
采用前向迭代器 iterator 对链表进行遍历。
//定义元素为整型的 list 对象,当前没有元素
list<int> l;
//在链表尾部插入新元素,链表自动扩张
l.push_back(2);
l.push_back(1);
l.push_back(5);
//在链表头部插入新元素,链表自动扩张
l.push_front(8);
//在任意位置插入新元素,链表自动扩张
list<int>::iterator it;
it=l.begin();
it++;//注意,链表的迭代器只能进行++或--操作,而不能进行+n 操作
l.insert(it,20);
//使用前向迭代器遍历链表
for(it=l.begin();it!=l.end();it++)
{
cout<<*it<<" ";
}
采用反向迭代器 reverse_iterator 对链表进行反向遍历。
//定义元素为整型的 list 对象,当前没有元素
list<int> l;
//在链表尾部插入新元素,链表自动扩张
l.push_back(2);
l.push_back(1);
l.push_back(5);
//反向遍历链表
list<int>::reverse_iterator rit;
for(rit=l.rbegin();rit!=l.rend();rit++)
{
cout<<*rit<<" ";
}
//5 1 2
(1)可以使用 remove()方法删除链表中一个元素,值相同的元素都会被删除。
//删除值等于 10 的所有元素
l.remove(10);
(2)使用 pop_front()方法删除链表首元素,使用 pop_back()方法删除链表尾元素。
//删除首元素
l.pop_front();
//删除尾元素
l.pop_back();
(3)使用 erase()方法删除迭代器位置上的元素。
list<int>::iterator it;
l.erase(it.begin()+2);//删除第二个元素
(4)使用 clear()方法清空链表。
//清空链表
l.clear();
采用 find()查找算法可以在链表中查找元素,如果找到该元素,返回的是该元素的迭代器位置;如果没有找到,则返回 end()迭代器位置。
find()算法需要声明头文件包含语句“#include
//定义元素为整型的 list 对象,当前没有元素
list<int> l;
//在链表尾部插入新元素,链表自动扩张
l.push_back(2);
l.push_back(8);
l.push_back(1);
l.push_back(5);
l.push_back(1);
//遍历链表
list<int>::iterator it,it2;
for(it=l.begin();it!=l.end();it++)
{
cout<<*it<<" ";
}
//回车换行
cout<//采用 find()查找算法在链表中查找
it=find(l.begin(),l.end(),5);
if(it!=l.end())//找到
{
cout<<"find it"<else
{
cout<<"not find it"<10);
if(it!=l.end())//找到
{
cout<<"find it"<else
{
cout<<"not find it"</**
2 8 1 5 1
find it
not find it
**/
采用 sort()方法可以对链表元素进行升序排列。
//定义元素为整型的 list 对象,当前没有元素
list<int> l;
//在链表尾部插入新元素,链表自动扩张
l.push_back(2);
l.push_back(8);
l.push_back(1);
l.push_back(5);
l.push_back(1);
//遍历链表
list<int>::iterator it,it2;
for(it=l.begin();it!=l.end();it++)
{
cout<<*it<<" ";
}
//回车换行
cout<//使用 sort()方法对链表排序,是升序排列
l.sort();
//遍历链表
for(it=l.begin();it!=l.end();it++)
{
cout<<*it<<" ";
}
/**
2 8 1 5 1
1 1 2 5 8
**/
采用 unique()方法可以剔除连续重复元素,只保留一个。
//定义元素为整型的 list 对象,当前没有元素
list<int> l;
//在链表尾部插入新元素,链表自动扩张
l.push_back(2);
l.push_back(8);
l.push_back(1);
l.push_back(1);
l.push_back(1);
l.push_back(5);
l.push_back(1);
//遍历链表
list<int>::iterator it,it2;
for(it=l.begin();it!=l.end();it++)
{
cout<<*it<<" ";
}
//回车换行
cout<//剔除连续重复元素(只保留一个)
l.unique();
//遍历链表
for(it=l.begin();it!=l.end();it++)
{
cout<<*it<<" ";
}
/**
2 8 1 1 1 5 1
2 8 1 5 1
**/
bitset 容器是一个 bit 位元素的序列容器,每个元素只占一个 bit 位,取值为 0 或 1,因而很节省内存空间。
使用 bitset,需要声明头文件包含语句“#include
创建 bitset 对象时,必须要指定容器的大小。 bitset 对象的大小一经定义,就不能修改了。
bitset<100000> b;
//定义了 bitset 对象 b, 它能容纳 100 000 个元素, 即 100 000 个 bit (位),此时,所有元素的值都为 0。
(1)采用下标法。
bitset<10> b;
//采用下标法给元素赋值
b[1]=1;
b[6]=1;
b[9]=1;
//下标法输出所有元素,第 0 位是最低位,第 9 位是最高位
//1001000010
(2)采用 set()方法,一次性将元素设置为 1。
bitset<10> b;
//采用 set()方法,一次性将元素设置为 1
b.set();
//下标法输出所有元素,第 0 位是最低位,第 9 位是最高位
int i;
for(i=b.size()-1;i>=0;i--)
{
cout<//1111111111
(3)采用 set(pos)方法,将某 pos 位设置为 1。
bitset<10> b;
//采用 set(pos)方法,将元素设置为 1
b.set(1,1);
b.set(6,1);
b.set(9,1);
//下标法输出所有元素,第 0 位是最低位,第 9 位是最高位
//1001000010
(1)采用下标法输出元素。
bitset<10> b;
//采用 set()方法,将元素全部设置为 1
b.set();
//采用 set(pos)方法,将元素设置为 0
b.set(0,0);
b.set(2,0);
b.set(3,0);
b.set(4,0);
b.set(5,0);
b.set(7,0);
b.set(8,0);
//下标法输出所有元素,第 0 位是最低位,第 9 位是最高位
int i;
for(i=b.size()-1;i>=0;i--)
{
cout<//1001000010
(2)直接向输出流输出全部元素。
bitset<10> b;
//采用 set()方法,将元素全部设置为 1
b.set();
//采用 set(pos)方法,将元素设置为 0
b.set(0,0);
b.set(2,0);
b.set(3,0);
b.set(4,0);
b.set(5,0);
b.set(7,0);
b.set(8,0);
//直接向输出流输出全部元素
cout<//1001000010
stack 堆栈是一个后进先出(Last In First Out, LIFO)的线性表,插入和删除元素都只能在表的一端进行。插入元素的一端称为栈顶(Stack Top),而另一端则称为栈底(StackBottom)。插入元素叫入栈(Push),元素的删除则称为出栈(Pop)。
要使用 stack,必须声明头文件包含语句“#include
堆栈的使用方法
堆栈只提供入栈、出栈、栈顶元素访问和判断是否为空等几种方法。
- 采用 push()方法将元素入栈;采用 pop()方法出栈;
- 采用 top()方法访问栈顶元素;
- 采用empty()方法判断堆栈是否为空,如果是空的,则返回逻辑真,否则返回逻辑假;
- 采用 size()方法返回当前堆栈中有几个元素.
//定义堆栈 s,其元素类型是整型
stack<int> s;
//元素入栈
s.push(1);
s.push(2);
s.push(3);
s.push(9);
//读取栈顶元素
cout<//返回堆栈元素数量
cout<//判断堆栈是否为空
cout<//所有元素出栈(删除所有元素)
while(s.empty()!=true)//堆栈非空
{
cout<" ";//读取栈顶元素
s.pop();//出栈(即删除栈顶元素)
}
/**
9
4
0
9 3 2 1
**/
queue 队列容器是一个先进先出(First In First Out, FIFO)的线性存储表,元素的插入只能在队尾,元素的删除只能在队首
使用 queue 需要声明头文件包含语句“#include
queue 队列的使用方法
queue 队列具有入队 push()(即插入元素)、出队 pop()(即删除元素)、读取队首元素 front()、读取队尾元素 back()、判断队列是否为空 empty()和队列当前元素的数目 size()这几种方法。
//定义队列,元素类型是整型
queue<int> q;
//入队,即插入元素
q.push(1);
q.push(2);
q.push(3);
q.push(9);
//返回队列元素数量
cout<//队列是否为空,是空,则返回逻辑真,否则返回逻辑假
cout<//读取队首元素
cout<//读取队尾元素
cout<//所有的元素出列(删除所有元素)
while(q.empty()!=true)
{
cout<" ";
//队首元素出队(删除队首元素)
q.pop();
}
/**
4
0
1
9
1 2 3 9
**/
priority_queue 优先队列容器与队列一样,只能从队尾插入元素,从队首删除元素。但它有一个特性,就是队列中最大的元素总是位于队首,所以出队时,并非按先进先出的原则进行,而是将当前队列中最大的元素出队。这点类似于给队列里的元素进行了由大到小的顺序排序。元素的比较规则默认为按元素的值由大到小排序;当然,可以重载“<”操作符来重新定义比较规则。
使用 priority_queue 需要声明头文件包含语句“#include
优先队列包含入队 push()(插入元素)、出队 pop()(删除元素)、读取队首元素 top()、判断队列是否为空 empty()和读取队列元素数量 size()等方法。
//定优先队列,元素类型为整型
priority_queue<int> pq;
//入队,插入新元素
pq.push(1);
pq.push(2);
pq.push(3);
pq.push(9);
//返回队列中元素数目
cout<//所有元素出队,删除所有元素
while(pq.empty()!=true)
{
//读取当前队首元素
cout<" ";
//出队,删除队首元素
pq.pop();
}
/**
4
9 3 2 1
**/
如果优先队列的元素类型是结构体,可以通过在结构体中重载“<”操作符的方法来修改优先队列的优先性。
//定义结构体
struct Info{
string name;
float score;
//重载“<”操作符,指定优先规则(排序规则)
bool operator < (const Info &a) const
{
//按 score 由小到大排列。如果要由大到小排列,使用“>”号即可
return a.score//定义优先队列,元素类型为 Info 结构体
priority_queue pq;
//定义结构体变量
Info info;
//入队
info.name="Jack";
info.score=68.5;
pq.push(info);
info.name="Bomi";
info.score=18.5;
pq.push(info);
info.name="Peti";
info.score=90;
pq.push(info);
//元素全部出队
while(pq.empty()!=true)
{
//返回队首元素
cout<" : "<//出队,删除队首元素
pq.pop();
}
/**
Bomi : 18.5
Jack : 68.5
Peti : 90
**/
如果优先队列的元素不是结构体类型,则可以通过重载“()”操作符的方式来定义优先级。当然,元素是结构体类型,也可以通过重载“()”操作符的方式来定义优先级,而不是一定要在结构体内重载“<”操作符才行。
//重载“()”操作符
struct myComp
{
bool operator()(const int &a,const int &b)
{
//由小到大排列采用“>”号;如果要由大到小排列,则采用“<”号
return a>b;
}
};
//定义优先队列,元素类型为 Info 结构体,显式说明内部结构是 vector
priority_queue<int,vector<int>,myComp> pq;
//入队
pq.push(1);
pq.push(9);
pq.push(2);
pq.push(30);
//元素全部出队
while(pq.empty()!=true)
{
//返回队首元素
cout<" ";
//出队,删除队首元素
pq.pop();
}
/**
1 2 9 30
**/