一、容器分类
二、序列式容器的创建
三、序列式容器的访问
四、序列式容器的修改
五、序列式容器元素的容量
六、set/multiset
七、map/multimap
容器是STL的基础,容器分2种:序列式容器(sequential container)、关联式容器(associative container)
序列式容器会强调元素的次序,依次维护第一个元素、第二个元素……,直到最后一个元素,面向序列式容器的操作主要是迭代操作。
一、容器分类:
序列式容器(sequential container):
vector //向量
deque //双向队列
list //线性表
关联式容器(associative container):
Set //集合 采用红黑树来做的。存放关键字。 关键字不重复。
MultiSet //多集合 采用红黑树来做的。存放关键字。 关键字可重复。
Map //映射 采用红黑树来做的。存放键值对。 关键字不重复。
MultiMap //多映射 采用红黑树来做的。 存放键值对。 关键字可重复。
所有的容器都支持的方法:
ob.begin() //返回一个迭代器,该迭代器指向容器的第一个元素
ob.end() //返回一个迭代器,该迭代器指向容器的最后一个元素的下一个元素
empty
ob.size() //返回元素个数
max_size
clear
insert
erase
ob.swap(ob2) //交换ob和ob2的内容
ob1==ob2 //如果ob1和ob2长度相同,且每个对应元素都相等,返回true
ob1 !=ob2 //!(ob1==ob2)
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
二、序列式容器的创建:6种方式
(1)创建空的容器,此时容器中的元素个数为0。
vector<int> obV;
//直接创建空的。
list<float> obL;
deque<double> obD;
(2) 产生特定大小的容器。
vector<double> obV(10);
//产生特定大小的容器。此时容器中的元素被创建,编译器使用默认值为元素隐式初始化,像int、float
和double等内置的数据类型会被初始化为0,对于类对象元素,将调用其无参构造函数(用户定义的或编
译器缺省提供的)或每个参数都有默认值的构造函数。此例中,obV中含10个double型元素, 初始化为0.
list<int> obL(20);
//创建list对象,含20个int型元素, 初始化为0
deque<float> obD(30);
//创建deque对象,含30个float型元素, 初始化为0
(3) 创建特定大小的容器,并且为其中的每个元素指定初始值
vector<int> obV(10,8);
//10个int型元素, 每个都初始化为8
list<double> obL(20,3.1);
//20个double型元素, 每个都初始化为3.1
deque<string> obD(30,"Hello");
//30个string型元素, 每个都初始化为"Hello"
(4)根据已有同类型的容器创建新容器,并将其中的元素完全复制过来
vector<int> obV2(obV1);
//或vector<int> obV2=obV1;
list<int> obL2(obL1);
//或list<int> obL2=obL1;
deque<int> obD2(obD1);
//或deque<int> obD2=obD1;
(5)通过数组
int sz[5]={11,2,3,4,45};
vector<int> obV(sz, sz+5);
list<int> obL(sz, sz+5);
deque<int> obD(sz, sz+5);
(6)C++11新特性
vector<int> vec1={1,2,3,4,5};
list<int> vec2={1,2,3,4,5};
deque<int> vec3={1,2,3,4,5};
三、序列式容器的访问
vector和deque类的容器创建后就可以通过容器名[下标]或容器名.at(序号)的形式对元素进行随机访问(这是因为这2种类模板对下标运算符[]进行了重载);
也支持迭代器访问。
list类的容器不支持下标运算符[],无法使用[]对元素进行随机访问。但支持双向迭代器访问。
vector deque list
at 1 1 0
operator[] 1 1 0 //下标访问运算符
迭代器访问 1 1 1
front 1 1 1 //获取容器头部元素。
back 1 1 1 //获取容器尾部元素。
例1:
#include <iostream>
#include <vector>
#include <list>
#include <deque>
using std::cout;
using std::endl;
using std::vector;
using std::list;
using std::deque;
int test
Deque(void){
//实验vector和deque
double arr1[5] = {1, 2, 3, 4, 5};
deque<double> dequeDbl(arr1, arr1 + 5);
//左闭右开。写[arr1[0],arr1[5]) ,则只有arr1[0],arr1[1],arr1[2],arr1[3],arr1[4]入队
for(int idx = 0; idx != dequeDbl.size(); ++idx){
//访问方式一:利用下标运算符随机访问
cout <<
dequeDbl[idx] << " ";
}
cout << endl;
deque<double>::iterator dit = dequeDbl.begin();
//访问方式二:迭代器
for(; dit != dequeDbl.end(); ++dit){
cout << *dit << " ";
}
cout << endl;
dit = dequeDbl.end();
--dit;
//因为.end()指向最后一个元素的下一个元素。所以--
for(; dit != dequeDbl.begin() -1; --dit){
//因为.begin()执行第一个元素,判断到第一个时,还是要打印,所以-1
cout << *dit << " ";
}
cout << endl;
for(auto & elem : dequeDbl){
//访问方式三:c++11 新特性,auto关键字
cout << elem << " ";
}
cout << endl;
return 0;
}
int testList(){
list<float> listFlt(3, 5);
listFlt[0] = 10;
//错误。list容器没有重载下标访问运算符,不支持随机访问。
list<float>::iterator lit;
for(lit = listFlt.begin(); lit != listFlt.end(); ++lit){
//通过迭代器访问。
(*lit) += 2;
cout << *lit << " ";
}
cout << endl;
list<float> listFlt2(4, 9); //4个元素,初始为9
listFlt.swap(listFlt2); //交换元素。进行交换,交换并没有移动数据,只是交换了指针而已
for(lit = listFlt.begin();lit != listFlt.end(); ++lit){
(*lit) += 2;
cout << *lit << " ";
}
cout << endl;
return 0;
}
int main(void){
testDeque();
testList();
return 0;
}
例2:
int main(){
vector<int> vecInt;
vec.reserve(10);
//预先开辟10个空间
vecInt[5]=3;
//错误。因为vetInt中没有数据。
}
四、序列式容器元素的修改
vector deque list
push_bak 1 1 1 //在容器尾部进行插入
pop_bak 1 1 1 //在容器尾部进行删除
push_front 0 1 1 //在容器头部进行插入。
vector是数组,不能在头部插入。
pop_front 0 1 1 //在容器头部进行删除。
vector是数组,不能再头部删除。
insert 1 1 1 //在容器中间插入元素
erase 1 1 1 //删除容器中间的元素
clear 1 1 1 //将容器中的对象清空。不是将元素设为0或什么,而是删掉。清空之后就不能访问这个元素了
swap 1 1 1
insert函数的使用
iterator insert(iterator p, elemType t);
//将元素t插到p之前,返回的迭代器指向被插入的元素。
void insert(iterator p, int n, elemType t);
//在p之前插入n个t,无返回值。
void insert(iterator p, iterator first, iterator last);
//在p之前插入[first, last)之间的所有元素。
对于vector而言,使用insert的时候元素要后移,时间复杂度是O(n), 尽量避免使用。
例1:
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::vector;
template <typename T>
//函数模板。不管是哪种容器,都可以调用display方法。
void display(T & c){
typename T::iterator cit;
//typename关键字告诉编译器iterator是一个类类型,而不是其它的。
for(cit = c.begin(); cit != c.end(); ++cit){
cout << *cit << " ";
}
cout << endl;
}
int main(void){
vector<int> vecInt(5, 0);
//5个元素,初始为0
display(vecInt);
//0 0 0 0 0
vector<int>::iterator it = vecInt.end();
//获取迭代器的位置。指向最后一个元素的下一个元素。
it = vecInt.
insert(it, 1);
//在it之前插入元素1,也就是在最后一个元素之后插入1。然后迭代器指向被插入的元素
display(vecInt);
//0 0 0 0 0 1
cout << "*it = " << *it << endl;
//因为迭代器指向插入的元素,所以为1
vecInt.insert(it, 2, 3);
//在it的位置之前插入2个3。
display(vecInt);
//0 0 0 0 0 3 3 1
it = vecInt.begin();
//it移动到头部位置
int arr[3] = {7, 8, 9};
vecInt.insert(it, arr, arr + 3);
//在it之前插入arr[0]到arr[3]之间的元素(前闭后开),即插入arr[0],arr[1],arr[2]
display(vecInt);
//7 8 9 0 0 0 0 0 3 3 1
cout << endl;
return 0;
}
erase函数的使用
iterator erase(iterator p);
//删除迭代器p所指向的元素,返回p指向的下一个迭代器
iterator erase(iterator first, iterator last);
//删除[frist,last)之间的所有元素,返回last指向的下一个迭代器
五、序列式容器元素的容量
vector deque list
empty 1 1 1
size 1 1 1
//当前vec拥有多少个元素
max_size 1 1 1
resize(count) 1 1 1
//调整容器大小,使其能够容纳count个元素。若当前大小<count,则追加额外元素,并初始化。
若当前大小>count,则容器会缩小到仅包含前count个元素。
cpapcity 1 0 0
//容量。当前vec最多可以存放多少元素
reserve(10); 1 0 0
//预先开辟一定的空间
shrink_to_fit 1 1 0
//删除未使用的容量。C++11的用法。
resize函数的使用
例1:
#include <iostream>
#include <vector>
int main (){
std::vector<int> foo (
100); //产生100个元素的vector,初始化为0
std::cout << foo.capacity() << std::endl; //100
foo.resize(10);
//调整容器大小为10。容器会缩小到仅包含前10个元素。但是capacity的值仍然为100
std::cout << foo.size() << endl; //10。只有10个元素(都为0)
std::cout << foo.capacity() << std::endl; //100。capacity值为100
return 0;
}
例2:
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::vector;
int main(void){
vector<int> vecInt = {1, 2, 3};
cout << vecInt.capacity() << endl; //3
vecInt.resize(10);
//调整容器大小为10。会追加额外元素,并初始化。
for(auto & elem : vecInt)
cout << elem << " ";
//1 2 3 0 0 0 0 0 0 0
cout << vecInt.capacity() << endl;
//10。元素个数长了,容量也会跟着涨。
return 0;
}
shrink_to_fit函数的使用
例1:
#include <iostream>
#include <vector>
int main (){
std::vector<int> foo (100); //产生100个元素的vector,初始化为0.
std::cout << foo.capacity() << std::endl; //100。
foo.resize(10); //调整容器大小为10.
std::cout << foo.capacity() << std::endl; //100。capacity值为100,但是只有10个元素(都为0)。
foo.shrink_to_fit()
;
std::cout << foo.capacity() << std::endl;
//10。删除多余的容量(后面90个)。
return 0;
}
例2:
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::vector;
int main(void){
vector<int> vecInt = {1, 2, 3};
cout << vecInt.capacity() << endl; //3。容量为3
vecInt.resize(100); //100。调整容器大小为100
cout << vecInt.capacity() << endl; //100。
vecInt.shrinke_to_fit();
//因为当前有100个元素(最后一堆0也是数据),所以没有未使用的容量要删
cout << vecInt.capacity() << endl; //100。打印出还是100。
vecInt.clear();
//把数据清空,此时capacity还是100。相当于vecInt.resize(0);
cout << vecInt.capacity() << endl; //100。
vecInt.shrink_to_fit();
//容量为100,当前无元素。所以可以删除未使用的容量。
cout << vecInt.capacity() << endl;
//0 。删除未使用的容量后,容量变为了0
return 0;
}
例3:不使用C++11的shrink_to_fit方法,而是使用swap函数实现未使用容量的删除。
#include <iostream>
#include <vector>
int main(){
vec.reserve(100);
//预先开辟100空间的大小
vecInt.push_back(1);
vecInt.push_back(2);
vecInt.push_back(3);
cout << vecInt.size() << endl; //3
cout << vecInt.capacity() << endl; //100
vector<int>(vecInt).swap(vecInt);
//或 vecInt.swap(vector<int>(vecInt)); 等价于调用C++11的vecInt.shrink_to_fit();函数。
vector<int>(vecInt)表示产生一个临时的vector,并把vecInt中的元素拷贝过来。所以临时
的vector就是一个含有3个元素,并且容量为3的vector。然后这个临时的对象调用swap
函数,与vecInt进行了交换。这样vecInt就成了含有3个元素,容量为3的vector,而临时
的对象就成了含有3个元素,容量为100的vector。执行完这句话之后,临时的vector释
放掉了。
for(auto & elem : vecInt)
cout << elem << " "; //1 2 3
cout << endl;
cout << vecInt.capacity() << endl;
//3。执行完之后,容量变成了3
return 0;
}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
关联式容器
set 采用红黑树来做的。存放关键字。 关键字不重复。
multiset 采用红黑树来做的。存放关键字。 关键字可重复。
map 采用红黑树来做的。存放键值对。 关键字不重复。
multimap 采用红黑树来做的。存放键值对。 关键字可重复。
六、set/multiset
例1:set的创建
#include <iostream>
#include <set>
using std::cout;
using std::endl;
using std::set;
int main(void){
int arr[9] = {2, 1, 3, 5, 4, 6, 3, 5, 6};
set<int> setInt(arr,arr+9);
//创建一个set。默认情况下采用"<"排序,即set<int, std::less<int> > setInt(arr, arr + 9);
cout << setInt.size() << endl;
//set和map并没有capacity方法.因为是用红黑树实现的,可以一直往里添加。
set<int>::iterator sit;
for(sit = setInt.begin(); sit != setInt.end(); ++sit)
//set不支持[]下标式的随机访问,必须通过迭代器访问元素。
cout << *sit << " ";
//1 2 3 4 5 6 。放了9个元素只存放了6个.这说明set会过滤掉已经存放在其中的元素。
同时会对元素进行排序。默认情况下采用"<"进行排序。
cout << endl;
return 0;
}
例2:采用">"的排序方式进行元素的创建
#include <iostream>
#include <set>
using std::cout;
using std::endl;
using std::set;
int main(void){
int arr[9] = {2, 1, 3, 5, 4, 6, 3, 5, 6};
set<int,
std::greater<int>> setInt(arr,arr+9);
//创建一个set。不采用默认的方式,而是采用">"的方式。
cout << setInt.size() << endl;
set<int>::iterator sit;
for(sit = setInt.begin(); sit != setInt.end(); ++sit)
cout << *sit << " ";
//6 5 4 3 2 1
cout << endl;
return 0;
}
例3:自定义类型的排序,需要自定义">","<"运算符。
#include <iostream>
#include <set>
using std::cout;
using std::endl;
using std::set;
class Point{
friend std::ostream & operator << (std::ostream & os, const Point &rhs);
public:
Point(int ix, int iy)
: _ix(ix)
, _iy(iy)
{
}
int product() const{ //坐标的乘积
return _ix * _iy;
}
private:
int _ix;
int _iy;
};
std::ostream & operator << (std::ostream & os, const Point &rhs){
//重载输出运算符。
os << "(" << rhs._ix<< "," << rhs._iy<< ")";
}
bool operator
>(const Point & lhs, const Point & rhs){
//重载大于符号。
return lhs.product() > rhs.product();
//如果lhs坐标的乘积>rhs坐标的乘积,返回true
}
int main(void){
Point arr[4] = {
Point(1, 2),
Point(3, 4),
Point(-1, 2),
Point(4, 5)
};
//自定义排序函数
set<Point,
std::greater<Point> > setPoint(arr, arr + 4);
//创建一个集合,存放Point类型。
不采用默认的方式,采用">"的方式。
set<Point,
std::greater<Point> >::iterator sit;
//迭代器
for(sit = setPoint.begin();sit != setPoint.end();++sit){
cout << * sit << endl;
//*sit是一个Point,所以要重载输出运算符。结果:(4,5) (3,4) (1,2) (-1,2)
}
return 0;
}
例4:multiset与set不同之处在于其允许出现相同的关键字。
#include <iostream>
#include <set>
using std::cout;
using std::endl;
using std::multiset;
int main(void){
int arr[9] = {2, 1, 3, 5, 4, 6, 3, 5, 6};
multiset<int, std::greater<int> > msetInt(arr, arr + 9);
cout << "msetInt's size: " << msetInt.size() << endl;
multiset<int>::iterator sit;
for(sit = msetInt.begin(); sit != msetInt.end(); ++sit)
cout << *sit << " ";
cout << endl;
return 0;
}
七、map/multimap
map中存放的是pair.
multimap与map的区别是关键字可以相同。
例1:pair
#include <iostream>
#includde <utility>
//pair单独使用时用到的头文件
using std::cout;
using std::endl;
using std::pair;
using std::string;
int main(void){
pair<string, string> t("600036", "招商银行");
//键值对。"600036"是键,"招商银行"是值。
cout << t.first << "-->" << t.second << endl;
//600036-->招商银行。pair是用结构体定义的。first和second的访问权限是public。
return 0;
}
例2:map的创建和使用
#include <iostream>
#include <string>
#include <map>
using std::cout;
using std::endl;
using std::pair;
using std::string;
using std::map;
int main(void){
pair<int, string> pairArr[4] ={
//创建pair的数组
pair<int, string>(1, "北京"),
pair<int, string>(2, "深圳"),
pair<int, string>(3, "广州"),
pair<int, string>(2, "苏州"),
};
map<int, string> mapi2str(pairArr, pairArr + 4);
//创建map。map里面不允许有相同关键字的pair元素。关键字相同时会
自动过滤掉。因为这里有两个关键字2,所以第二个键值对会过滤掉。同
时会根据关键字进行排序。
map<int, string>::iterator mit;
//通过迭代器方式访问。
for(mit = mapi2str.begin();mit != mapi2str.end();++mit){
cout << mit->first << "-->"<< mit->second << endl;
//输出:1-->北京 2-->深圳 3-->苏州 。会根据关键字进行排序。
}
return 0;
}
例子3:map支持下标式的访问运算符。
#include <iostream>
#include <string>
#include <map>
using std::cout;
using std::endl;
using std::pair;
using std::string;
using std::map;
int main(void){
pair<string, string> pairArr[4] ={
pair<string, string>("1", "北京"),
pair<string, string>("2", "深圳"),
pair<string, string>("3", "广州"),
pair<string, string>("2", "苏州"),
};
map<string, string> mapi2str(pairArr, pairArr + 4);
cout << "2-->" << mapi2str["2"] << endl;
//
输出2-->深圳。
map支持[]下标式的随机访问.set/multiset/multimap都 不
支持。由此k看
出:
["2"]的"2"
代表的是关键字,不是数字。
return 0;
}
例4:可以通过下标访问和修改map的值
#include <iostream>
#include <string>
#include <map>
using std::cout;
using std::endl;
using std::pair;
using std::string;
using std::map;
int main(void){
map<string, string> mapss; //创建map
mapss["11"] = "aa";
//没有相应关键字时,直接添加
mapss["22"] = "bb";
mapss["33"] = "cc";
mapss["33"] = "dd";
//有相应关键字时,直接修改value的内容
for(auto & elem : mapss){
//使用auto关键字的方式访问。输出:11-->aa 22-->bb 33-->dd
cout << elem.first << "-->"<< elem.second << endl;
}
return 0;
}
例5:multimap允许关键字重复
#include <iostream>
#include <map>
#include <string>
using std::cout;
using std::endl;
using std::multimap;
using std::pair;
using std::string;
int main(void){
pair<int, string> pairArr[4] ={
pair<int, string>(9, "Asia"),
pair<int, string>(4, "Africa"),
pair<int, string>(1, "Euro"),
pair<int, string>(4, "America")
};
multimap<int, string> mmapi2str(pairArr, pairArr + 4);
cout << mmapi2str.size() << endl;
//4
for(auto & elem : mmapi2str){
cout << elem.first << "-->" << elem.second << endl;
//1-->Euro 4-->Africa 4-->America 9-->Asia。关键字重复很可能采用链表
的方式往上挂。
}
return 0;
}
stl萃取技术
迭代器模式
适配器模式
unordermap:
时间复杂度o(a)
补充:unordered_map