复习笔记——C++中的容器(STL容器)
Posted 别呀
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了复习笔记——C++中的容器(STL容器)相关的知识,希望对你有一定的参考价值。
STL定义
STL(Standard Template Library),即标准模板库,是一个高效的C++程序库。被容纳于C++标准程序库(C++ Standard Library)中,是ANSI/ISO C++标准中最新的也是极具革命性的一部分。包含了诸多在计算机科学领域里常用的基本数据结构和基本算法。为广大C++程序员们提供了一个可扩展的应用框架,高度体现了软件的可复用性
从逻辑层次来看,在STL中体现了泛型化程序设计的思想(generic programming)。在这种思想里,大部分基本算法被抽象,被泛化,独立于与之对应的数据结构,用于以相同或相近的方式处理各种不同情形。
从实现层次看,整个STL是以一种类型参数化(type parameterized)的方式实现的基于模板(template)。
容器分为两大类:顺序容器、关联容器。
顺序容器
顺序容器有以下三种:vector(向量)、 deque(双队列)、list(列表)。
它们之所以被称为顺序容器,是因为元素在容器中的位置同元素的值无关,即容器不是排序的。将元素插入容器时,指定在什么位置(尾部、头部或中间某处)插入,元素就会位于什么位置。
顺序容器的接口:
①插入方法
push_front(),push_back(),insert(),运算符“=”
②删除方法
pop() ,erase(),clear()
③迭代访问方法
使用迭代器
④其他顺序容器访问方法(不修改访问方法)
front(),back(),下标[]运算符
1、vector
向量属于顺序容器,用于容纳不定长线性序列(即线性群体),提供对序列的快速随机访问(也称直接访问)。
数据结构很像一个数组,所以与其他容器相比,vector 能非常方便和高效访问单个元素,支持随机访问迭代子。
向量是动态结构,它的大小不固定,可以在程序运行时增加或减少与数组不同,向量的内存用尽时,向量自动分配更大的连续内存区,将原先的元素复制到新的内存区,并释放旧的内存区。这是向量类的优点。
头文件:#include <vector>
基本操作:
(1)头文件
#include<vector>
(2)创建vector对象
vector<int> vec;
(3)尾部插入数字
vec.push_back(a);
(4)使用下标访问元素
cout<<vec[0]<<endl;记住下标是从0开始的。
(5)使用迭代器访问元素
vector<int>::iterator it;
for(it=vec.begin();it!=vec.end();it++)
cout<<*it<<endl;
(6)插入元素
vec.insert(vec.begin()+i,a);在第i+1个元素前面插入a;
(7)删除元素
vec.erase(vec.begin()+2);删除第3个元素
vec.erase(vec.begin()+i,vec.end()+j);删除区间[i,j-1];区间从0开始
(8)向量大小
vec.size();
vec.resize;改变大小
(9)清空
vec.clear();
示例(输出质数):
#include <iostream>
#include <iomanip>
#include <vector> //包含向量容器头文件
using namespace std ;
int main(){
vector<int> A(10); //创建vector对象
int n;
int primecount = 0, i, j;
cout<<"Enter a value>=2 as upper limit: ";
cin >> n;
A[primecount++] = 2;//下标法访问元素
for(i = 3; i < n; i++){
if (primecount == A.size())
A.resize(primecount + 10); //改变容器大小
if (i % 2 == 0)
continue;
j = 3;
while (j <= i/2 && i % j != 0)
j += 2;
if (j > i/2) A[primecount++] = i;
}
for (i = 0; i<primecount; i++){//输出质数
cout<<setw(5)<<A[i];
if ((i+1) % 10 == 0) //每输出10个数换行一次
cout << endl;
}
cout<<endl;
return 0;
}
--------------
执行结果:
Enter a value>=2 as upper limit: 20
2 3 5 7 11 13 17 19
2、deque
双端队列是一种放松了访问权限的队列。元素可以从队列的两端入队和出队,也支持通过下标操作符“[]
”进行直接访问。
与向量的对比:
功能上:和向量没有多少区别,
性能上:在双端队列起点上的插入和删除操作快
头文件:#include <deque>
3、list
列表主要用于存放双向链表,可以从任意一端开始遍历。列表还提供了拼接(splicing)操作,将一个序列中的元素从插入到另一个序列中。
对比:
①元素的插入和删除操作对 list 而言尤为高效。
②与 vector 和 deque 相比,对元素的下标访问操作的低效是不能容忍的,因此 list 不提供这类操作。
头文件:#include <list>
示例:
#include <iostream>
#include <list>
using namespace std ;
int main(){
list<int> Link; //构造一个列表用于存放整数链表
int i, key, item;
for(i=0;i < 10;i++)// 输入10个整数依次向表头插入
{
Link.push_front(i);
}
cout<< "List:"; // 输出链表
list<int>::iterator p=Link.begin();
while(p!=Link.end()){ //输出各节点数据,直到链表尾
cout <<*p << " ";
p++; //使P指向下一个节点
}
cout << endl;
cout << "请输入一个需要删除的整数: ";
cin >> key;
Link.remove(key);
cout << "List: "; // 输出链表
p=Link.begin(); // 使P重新指向表头
while(p!=Link.end()){
cout <<*p << " ";
p++; // 使P指向下一个节点
}
cout << endl;
}
---------------------
执行结果示例:
List:9 8 7 6 5 4 3 2 1 0
请输入一个需要删除的整数: 3
List: 9 8 7 6 5 4 2 1 0
顺序容器的选择
a、若需要随机访问操作,则选择vector;
b、若已经知道需要存储元素的数目, 则选择vector;
c、若需要随机插入/删除(不仅仅在两端),则选择list
d、只有需要在首端进行插入/删除操作的时候,才选择deque,否则都选择vector。
e、若既需要随机插入/删除,又需要随机访问,则需要在vector与list间做个折中。
f、当要存储的是大型负责类对象时,list要优于vector;当然这时候也可以用vector来存储指向对象的指针,同样会取得较高的效率,但是指针的维护非常容易出错,因此不推荐使用。
注意:vector与deque的迭代器支持算术运算,list的迭代器只能进行++/–操作,不支持普通的算术运算。
关联容器
通过保存在数据项中的索引项,尽可能快速检索数据项。
STL标准库中只包含有序关联容器:set(集合)
、multiset(多重集合)
、map(映射)
、multimap(多重映射)
。
set
, multiset
:数据项就是索引项。 multiset
允许出现重复的索引项。
map
, multimap
:数据项是由索引项和其他某种类型的数据组成的一对数据。 multimap允许出现重复的索引项。
1、map
增加和删除节点对迭代器的影响很小。对于迭代器来说,可以修改实值,而不能修改key。
自动建立Key-value的对应。key 和 value可以是任意你需要的类型。
根据key值快速查找记录,查找的复杂度基本是Log(N),如果有1000个记录,最多查找10次,1,000,000个记录,最多查找20次。
map的构造函数:
使用map得包含map类所在的头文件
#include <map>
map对象是模板类,需要关键字和存储对象两个模板参数:
map<int, string> personnel;//用int作为索引,存储string对象
示例:
#include <map>
#include <iostream>
#include <string>
using namespace std;
int main(){
map< string, string > trans_map;
typedef map< string, string >::value_type valType;
trans_map.insert( valType( "001", "wang" ));
trans_map.insert( valType( "002", "li" ));
trans_map.insert( valType( "003", "luo" ));
trans_map.insert( valType( "004", "wu" ));
trans_map.insert( valType( "005", "zhao" ));
trans_map.insert( valType( "006", "sun" ));
trans_map.insert( valType( "007", "qian" ));
trans_map.insert( valType( "008", "long" ));
map< string,string >::iterator it;
cout << "Here is our transformation map: \\n";
for(it=trans_map.begin();it!=trans_map.end();++it)
cout<<"key: "<<(*it).first<<"\\t"<<"value: " <<(*it).second<<"\\n";
cout<<"Find Key:005"<<endl;
it=trans_map.find("005");
if (it==trans_map.end()){
cout<<"not found"<<endl;
}
else{
cout<<"key: "<<(*it).first <<"\\t"<<"value: " <<(*it).second<<"\\n";
}
return 0;
}
----------------------
执行结果:
Here is our transformation map:
key: 001 value: wang
key: 002 value: li
key: 003 value: luo
key: 004 value: wu
key: 005 value: zhao
key: 006 value: sun
key: 007 value: qian
key: 008 value: long
Find Key:005
key: 005 value: zhao
2、multimap
multimap 除了元素对的关键字不是唯一外,与 map 相似
头文件:#include <map>
3、set
set 可以被视为只有关键字而没有相关的元素值的 map,因此 set 的用户接口也发生了微小的变化:成员类型中没有:
①typedef Key value_type;
②typedef Cmp value_compare
③操作中没有元素的下标访问操作。
头文件:#include <set>
4、multiset
multiset 除了关键字不是唯一外,与 set 相似。
头文件:#include <set>
容器的函数
所有容器都有以下两个成员函数:
int size()
:返回容器对象中元素的个数。
bool empty()
:判断容器对象是否为空。
顺序容器和关联容器还有以下成员函数:
begin()
:返回指向容器中第一个元素的迭代器。
end()
:返回指向容器中最后一个元素后面的位置的迭代器。
rbegin()
:返回指向容器中最后一个元素的反向迭代器。
rend()
:返回指向容器中第一个元素前面的位置的反向迭代器。
erase(...)
:从容器中删除一个或几个元素。
clear()
:从容器中删除所有元素。
如果一个容器是空的,则 begin()
和 end()
的返回值相等,rbegin()
和 rend()
的返回值也相等。
顺序容器还有以下常用成员函数:
front()
:返回容器中第一个元素的引用。
back()
:返回容器中最后一个元素的引用。
push_back()
:在容器末尾增加新元素。
pop_back()
:删除容器末尾的元素。
insert(...)
:插入一个或多个元素。该函数参数较复杂,此处省略。
示例
(这里只列举一些容器以及一些函数用法)
顺序容器:
vector 容器
void main() //遍历、删除、插入
{
vector<int> v1;
v1.push_back(10); //copy 到申请的容器
v1.push_back(20);
v1.push_back(30);
//访问容器,定义一个迭代器: 相当于一个指针
//正向遍历
for(vector<int>::iterator it = v1.begin();it != v1.end();it++)
{
cout<<*it<<endl;
}
//逆向遍历
for(vector<int>::reverse_iterator rit = v1.rbegin();rit != v1.rend();rit++)
{
cout<<*rit<<endl;
}
//区间删除
v1.erase(v1.begin(),v1.begin()+2);
//指定位置删除
v1.erase(v1.begin());
//遍历删除
for(vector<int>::iterator dit = v1.begin();dit != v1.end();)
{
if(*dit == 2)
{
v1.erase(dit); //当删除迭代器所指向元素时,erase让it自下移
}
else
{
dit++;
}
}
//插入元素
v1.insert (v1.begin(),100); //头部插入数值100
v1.insert (v1.end (),200); //尾部插入数值200
return;
}
deque 容器
void printd(deque<int> &d)
{
for(deque<int>::iterator it = d.begin(); it != d.end();it++)
{
cout<<"元素"<<*it<<endl;
}
}
void main()
{
deque<int> d;
d.push_back(1); //在容器末尾增加新元素
d.push_back(2);
d.push_front(3); //在容器头尾增加新元素
d.push_front(4);
printd(d);
deque<int>::iterator it = find(d.begin(),d.end(),3); //查找指定元素3 找到时,返回该元素的迭代器;找不到时,返回end()。
if(it != d.end())
{
cout<<"3与第一个元素的距离为:"<<distance(d.begin (),it)<<endl;
}
}
关联容器:
set容器
void main()
{
set<int> set1;
//循环插入
for(int i = 0;i < 3;i ++)
{
int tmp = rand() % 5;
set1.insert(tmp);
}
//遍历打印集合
for(set<int>::iterator it = set1.begin();it != set1.end(); it++)
{
cout<<*it<<endl;
}
//删除集合
while(!set1.empty())
{
set<int>::iterator item = set1.begin();
set1.erase(item);
}
return;
}
map容器
//元素的添加、遍历、删除等基本操作
void main()
{
//1、创建map对象
map<int, string> map1;
//方法1 添加元素 pair是一个有两个元素的结构体,方便使用,类似map :键值可以随意使用
//pair可以用来当做map的键值来插入 pair<T1,T2> p1 创建一个空的pair对象,有两个元素T1和T2,采用值初始化
map1.insert(pair<int, string>(1,"hehe"));
map1.insert(pair<int, string>(2,"xixi"));
//方法2 添加元素 make_pair
//make_pair
map1.insert(make_pair(3,"haha"));
map1.insert(make_pair(4,"huhu"));
//方法3 添加元素
map1.insert(map<int, string>::value_type(5,"kaka"));
map1.insert(map<int, string>::value_type(6,"yiyi"));
//方法4 数组方式插入数据
map1[7] = "yaya";
//容器遍历(迭代器)
for(map<int, string>::iterator it = map1.begin();it != map1.end(); it++)
{
cout<<it->first<<"\\t"<<it->second <<endl;
}
cout<<"遍历结束以上是关于复习笔记——C++中的容器(STL容器)的主要内容,如果未能解决你的问题,请参考以下文章