容器
容器都是类模板,实例化后称为容器类,用容器类定义的对象称为容器对象
ps:用vector来解释一下
vector是一种顺序容器vector< int > 是容器类的名称vector< int > a则定义了一个容器类对象a
顺序容器
元素在容器中的位置与元素的值是无关的,容器不是排序的,可以在任和指定位置插入元素
顺序容器有vector(可变长数组),deque(双端队列),list(双向链表)
ps:vector和deque可以实现随机存取
vector
引用头文件vector
vector作为一种可变长的动态数组,可以根据需要自行分配空间,但是好像不会回收,用成员函数capacity()可以看出来。
下面是常用的成员函数:
size():返回元素个数
capacity():返回数组分配的空间
empty():检查是否为空,bool类型
end(),begin():返回数组的终止和起始迭代器
push_back(),pop_back():在数组的尾部添加或删除元素
insert(position, first, last),erase(sirst, last):向数组某一位置插入或删除若干元素
front(),back():返回对数组第一个和最后一个元素的引用
使用示例:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
int a[5] = {1, 2, 3, 4, 5};
vector<int> dyarray(a, a+5);
//函数原型是vector(iterator first, iterator last),相当于把其他容器的[first, last)赋值给vector容器
//iterator是指迭代器
cout << dyarray.end() - dyarray.begin() << endl;
cout << dyarray.size() << endl; //返回数组中元素的个数
cout << dyarray.empty() << endl; //(bool)是否为空
dyarray.push_back(111); //在数组的尾部添加
dyarray.pop_back(); //删除末尾的元素
dyarray.insert(dyarray.begin() + 1, a + 1, a + 3);
//向vector数组的某一位置插入若干元素,原型:insert(position, first, last)
dyarray.erase(dyarray.begin() + 1, dyarray.begin() + 3);
//删除vector数组某一位置的若干元素,原型:erase(first, last)
cout << dyarray.front() << " " << dyarray.back() << endl;
//front()返回对数组第一个元素的引用,back()返回最后一个
cout << dyarray.capacity() << endl;
//二维数组的声明
//主索引大小要给出(跟二维数组的声明是一样的),如果需要变长再手动加
vector< vector<int> > v(3);
for(int i = 0; i < 3;i++)
{
for(int j = 0;j < 5;j++)
v[i].push_back(j);
}
for(int i = 0; i < 3;i++)
{
for(int j = 0;j < 5;j++)
cout << v[i][j] << " ";
cout << endl;
}
cout << v.size() << endl; //返回值是3
return 0;
}
list
引用头文件list
list是对应双向链表的容器,增删元素只要修改指针即可,但是不能进行随机存取。
与vector有许多类似的成员函数,以及一些独有的成员函数,如下:
push_back(),pop_back():在尾部增删元素
push_front(),pop_front():在头部增删元素
sort():将链表中的元素按从小到大排序
与后面的算法sort不同,对于vector和deque这种支持随机访问的容器,可以直接用算法sort进行排序,但是list不支持随机访问,所以在成员函数中实现了排序
remove(const T &val):删除和常量参数相等的元素
unique():删除和前一个元素相同的元素(只有当队列有序的时候才能实现去重)
merge(list &x):将两个有序的链表合并,合并后依旧有序
splice(list::iterator i, list &x, first, last):将链表x的[first, last)剪切到原链表迭代器的位置之前
使用示例如下:
#include<iostream>
#include<list>
using namespace std;
void Printf(list<int> l)
{
for(list<int>::iterator i = l.begin();i != l.end();i++)
cout << *i << " ";
}
int main()
{
int a[5] = {1, 2, 3, 4, 5};
list<int> l(a, a + 5); //赋值加初始化,和vector一样
l.push_front(0); //在头部插入
l.push_back(5); //在尾部插入
l.pop_front(); //删除头部元素
l.remove(5); //删除与参数相等的元素,函数原型:void remove(const T &val)
int b[5] = {3, 4, 5, 5, 6};
list<int> l_2(b, b + 5);
l_2.reverse(); //将链表的排序逆向
l_2.sort(); //将队列从小到大排序
l.merge(l_2); //将两个有序的双向链表合并,合并后依旧有序
//l:1, 2, 3, 3, 4, 4, 5, 6
l.unique(); //删除所有和前一个元素相同的元素,对于已经有序的链表可以直接实现去重
//l:1, 2, 3, 4, 5, 6
l.push_back(3);
l.unique();
//l:1, 2, 3, 4, 5, 6, 3
l.insert(l.end(), a + 1, a + 2); //在某一位置插入,用法和vector相同
list<int>::iterator pos = find(l.begin(), l.end(), 6);
//可以使用find函数对链表进行查找,返回值是迭代器
l.splice(l.begin(), l_2, l_2.begin(), l_2.end());
//函数原型为:splice(list<T>::iterator, list<T> &x, first, end)
//将队列x的[first, last)剪切到原链表迭代器的位置之前
return 0;
}
deque
deque的实现形式同样是一种可变长的动态数组,与vector容器不同的是,vector容器在头部增删元素比较浪费时间(因为是顺序存储,除了在头部的操作外还要逐个移动后面的元素,因此时间复杂度就与元素个数有关)而deque在头部的增删元素也能像尾部一样,只用常数时间就能完成
deque的成员函数与vector进本没有区别,两个独有的在头部增删的成员函数:
push_front(),pop_front():在头部增删元素
关联容器
关联容器有一对键值对(key - value),key是键值,value是实值,容器中的元素按照键值根据某种规则排序(比如一般是从小到大)。
相比于顺序容器,关联容器内的元素是排序的,因为对元素的(键)值进行修改之后,容器不会对元素进行重新排序,所以修改元素要先删除原有的再重新添加新的元素。
关联容器有:
set:只有一组值,既是实值也是键值,不允许有重复元素
multiset:与set基本相同,允许有重复元素
map:map中的所有元素都是键值对(pair),不允许有重复的键值
multimap:与map基本相同,允许有重复元素
pair类模板
pair类模板拥有两个值,之前提过的键值对就可以看作是pair类,键值和实值就是pair的两个值。
为了避免大量重复的代码,我们引入了函数模板template,简单提示一下:
template <typename T>
T Max_Value(T first, T second)
{
if(first > second)
return first;
else return second;
}
当传给函数的参数是两种不同的数据类型时,编译器会由于找不到合适的匹配类型而报错,这是引入pair类模板有时可以解决一些问题。
pair的一些用法如下:
#include<iostream>
using namespace std;
int main()
{
pair<int, double> p_1; //p_1:<0, 0>
pair<int, int> p_2(pair<char, char>('a', 'b')); //p_2:<97, 98>
pair<int, string> p_2 = make_pair(200, "hello");
cout << p_1.first << "," << p_1.second << endl;
//用first和second可以访问pair类的两个值
return 0;
}
multiset
multimap
set
map
迭代器
迭代器类似于指针,通过迭代器可以访问容器中的元素;某些时候迭代器和数组中的索引很像
已经见过的,例如vector变量v的成员函数v.begin()就是迭代器的一种,返回的是容器第一个元素的“指针”
迭代器的定义方法:
■正向迭代器:
容器类名::iterator 迭代器名
ex:vector::iterator i
这是一个未初始化的vector容器的正向迭代器
■常量正向迭代器:
容器类名::const_iterator 迭代器名
■反向迭代器:
容器类名::reverse_iterator 迭代器名
■反向常量迭代器:
容器类名::const_reverse_iterator 迭代器名
迭代器的使用基本上和数组的索引,指针没有太大区别,++的使用也是一样的,但是正向迭代器和反向迭代器的方向相反。
示例:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
int a[5] = {1, 2, 3, 4, 5};
vector<int> v(a, a+5);
vector<int>::iterator i; //正向迭代器
for(i = v.begin();i < v.end();i++) //作为索引
cout << *i << " "; //类似指针
for(vector<int>::reverse_iterator j = v.rbegin();j != v.rend();j++)
//反向迭代器
cout << *i << " ";
return 0;
}
ps:反向迭代器循环继续的条件是j != v.rend(),这一点在双向迭代器中也是一样
事实上常用的迭代器有三种:正向,双向,随机访问
双向迭代器是有- -功能的;有随机访问功能的除了支持==,!=还支持用>, <等来比较
不同容器的迭代器功能如下:
容器 | 迭代器功能 |
---|---|
vector | 随机访问 |
deque | 随机访问 |
list | 双向 |
set/multiset | 双向 |
map/multimap | 双向 |
stack | 不支持迭代器 |
queue | 不支持迭代器 |
priority_queue | 不支持迭代器 |
算法
引用头文件algorithm
STL里有一些已经实现好的常用算法,最常使用的两个:
sort():将容器内的元素按照从小到大排序
值得注意的是,算法中的sort()函数只能对可以实现随机访问的容器进行排序,对于list,可以使用内部实现的成员函数来达到排序的目的
find(first, end, const T &val):在[first, end)区间内寻找与val相等的元素,返回值是指针类型(对于数组来说)或者迭代器(对于容器来说)
用法示例:
#include<iostream>
#include<algorithm> //使用STL的算法
#include<vector>
#include<list>
using namespace std;
int main()
{
int a[5] = {2, 1, 4, 3, 5};
sort(a, a + 5);
for(int i = 0 ;i < 5;i++)
cout << a[i] << " ";
cout << endl;
//1 2 3 4 5
vector<int> v(a, a + 5);
sort(v.begin(), v.end()); //算法中的sort()函数
list<int> l(a, a + 5);
l.sort(); //list容器的成员函数
int* pos_0 = find(a, a + 5, 5);
vector<int>::iterator pos_1 = find(v.begin(), v.end(), 5);
list<int>::iterator pos_2 = find(l.begin(), l.end(), 5);
//用find()函数,返回值是指针或者迭代器
cout << *pos_0 << " " << *pos_1 << " " << *pos_2 << endl;
return 0;
}