标准模板库
容器类
vector
以vector为例
vector <int> a = {1,2,3,4,5};//将数组{1,2,3,4,5}放入容器类实例中
vector <int> a = vector <int>(5); //创建一个有5个元素的容器类实例
vector <vector<int>> a = vector <vector<int>>(5);//创建一个有5个元素的容器类实例,且每个实例为都
//是一个容器类
创建一个容器类
cout<<a[1];
//2
vector <int> a = { 1,2,3,4,5 };
for (vector <int>::iterator i = a.begin(); i != a.end(); i++)
{
cout << *i << " ";
}
//1 2 3 4 5
vector <int>::iterator 为迭代器,其功能类似于指针
begin()返回一个指向容器中第一个元素的迭代器
end()返回一个表示超过容器尾的迭代器
push_back(T temp)将一个元素添加到容器类的末尾
erase(iterator a,iterator b)删除迭代器a指向的和迭代器b-1所指向的区间里的所有元素。[a,b)
clear() 清空vector中的元素,其size() 为0 ,但是capacity()则是不变
size() 返回元素个数
vector <int> old_v = { 1,6,7,8,9,10, };
vector <int> new_v = {5,2,3,4,5 };
old_v.insert(old_v.begin() + 1, new_v.begin() + 1, new_v.end());
for (vector <int>::iterator i = old_v.begin(); i != old_v.end(); i++)
{
cout << *i << " ";
}
//1 2 3 4 5 6 7 8 9 10
insert(iterator a,iterator b,iterator c) 在a处插入[b,c)区间的元素
list
list <int> a ={1,2,3,4,5};
list <int>b = { 6,7,8,9,10 };
a.remove(2);
for(int i:a)
cout<<i<<" ";
a.splice(a.begin(),b);
remove(const int & _val);删除所有值为_val的元素
remove_if(Pr1 _pred);删除一元谓词为true的元素
splice(iterator where, list b);将列表b剪切到a中,此时b中的元素将不复存在。
list <int>a = {1,2,2,3,4,5 };
a.unique();
unique()只能将相邻的重复的值压缩为一个值,可以先调用一次排序,再压缩来删除重复元素
sort() 将对列表进行排序
补:对于sort(),unique(),merge()还可以接受一个函数参数,这个函数称为谓词(返回为bool值)
list <int> a = { 2,5,4,3,2,1, };
list <int>::iterator last = remove(a.begin(), a.end(), 2);
a.erase(last, a.end());
for (int i : a)
cout << i << " ";
remove()会将没有删除的元素放到队首,并会返回一个迭代器,其分割了新元素和删除了元素
erase() 将第一和第二参数区间删除
valarray
valarray <int> a = { 2,5,4,3,2,1, };
cout << a.sum() << " ";
cout << a.min() << " ";
cout << a.max() << " ";
cout << a.size() << " ";
//17 1 5 6
sum() min() max() size() 功能如同函数名所示
resize() 并不是扩展内存大小,而是重新设定大小和数组中的值。
valarray 中没有begin() end()等迭代器的函数
valarray <int> a = { 2,5,4,3,2,1, };
for (int* i = begin(a); i != end(a); i++)
{
cout << *i << " ";
}
为了使valarray 也可以使用STL函数,使用begin(),end()函数返回一个迭代器(指针)
operator >() 其将会返回一个valarray 数组
alarray <int> b = a[slice(0, 3, 1)];
其中slice()为切片类,#1指定了起始位置,#2指定了个数,#3指定了步长
forward_list
它实现了单链表,它只需要正向迭代器,其更为简单、紧凑、但是功能也很少。
queue
队列,不允许随机访问队列元素,甚至不允许遍历队列
queue<int>a(deque<int> {1, 2, 3, 4, 5});
int top;
while (!a.empty())
{
top = a.front();
cout << top << " ";
a.pop();
}
empty() 若空则返回true,否则返回false
size() 返回队列中的元素个数
front() 返回指向队首元素的引用
back() 返回指向队尾元素的引用
push(const T & x) 在队尾插入x
pop() 删除队首元素
emplace() 与push相同,但其可以自己调用元素的构造函数。
priority_queue
它与队列不同的是,最大的元素总是放在队首,提供一个可选的函数参数,来选择比较的方式。
stack
栈,不多说了
empty() 若空则返回true,否则返回false
size() 返回栈中的元素个数
top() 返回指向栈顶元素的引用
push(const T & x) 在栈顶插入x
pop() 删除栈顶元素
array
它并非STL容器,其长度是固定的,所以其没有一些与长度有关的操作,如在数组尾加入一个元素,在某个位置插入一个元素。但是许多标准STL算法可以用于array对象,如copy(),for_each()
关联容器
其是值与键绑定在一起,类似于数据库中所设计的数据结构。
可以用键来查找值。
关联容器同普通的容器一样,它也可以插入新元素,但是不能指定插入的位置,因为它有自己的确定数据位置的算法,以便可以快速查找数据。关联容器一般是用树的数据结构实现的,其查找速度要快于链表。
set
它是关联集合,可反转,可排序,且键是唯一的,不能存储多个相同的值
constexpr int N = 7;
string a[N] = { "tdd","tea","apple","banana","clock","dog","tdd"};
set<string> b(a, a + N);
ostream_iterator <string, char> out(cout, " ");
copy(b.begin(), b.end(), out);
//apple banana clock dog tdd tea
set将一个迭代器区间作为参数来构造一个set
补:STL算法提供了一些数学上集合的函数,如并集,交集,差集(并集-交集),这些函数自然可以用于set对象。
set_union() 并集
set_intersection() 交集
set_difference() 差集
string strs1[7] = { "tdd","tea","apple","banana","clock","dog","tdd"};
string strs2[3] = { "tdd" ,"apple","banana" };
set<string> set1(strs1, strs1 + 7);
set<string> set2(strs2, strs2 + 3);
ostream_iterator <string, char> out(cout, " ");
set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(), out);
//apple banana tdd
set_intersection前两对参数为两个集合的范围,最后一个为输出到的迭代器。此输出迭代器可以是输出到屏幕的迭代器。但是不可以为set.begin(),因为其为常量迭代器,同时如果集合的大小不够,也会引发程序的崩溃,因此可以使用insert_iterator(c,c.begin())将复制转换为插入
string strs1[7] = { "tdd","tea","apple","banana","clock","dog","tdd"};
set<string> set1(strs1, strs1 + 7);
cout << *set1.lower_bound("c") << endl;
cout << *set1.upper_bound("c");
//clock
//clock
lower_bund(key) 返回第一个小于key的值
upper_bound(key) 返回第一个大于key的值
multimap
与set类似,其也是可以反转,经过排序的关联容器,但是键与值的类型不同,它的一个键可以关联多个值
multimap <int, string> codes;
创建一个新的multimap
pair<const int, string> item1(123, "BeiJing");
建立一个pair对象,其可以直接添加到multimap中
codes.insert(item1);
将item1添加到codes中
pair<const int, string> item2(123, "HeFei");
codes.insert(item2);
我们再添加一个
cout << codes.count(123) << endl;
count(key)将返回键key的所对的值的个数,如上将返回2;
lower_bund(key) 返回第一个小于key的值
upper_bound(key) 返回第一个大于key的值
同set一样,但是这里返回的iterator被解引用后为pair对象,所以有稍许的不同
equal_range(key) 这里返回一个pair对象,对象中有两个iterator对象,其包含了与key相等的空间。
无关联容器
对于容器的该井,无关联容器也是将值与键关联起来,但是其底层与关联容器不同的是它是基于数据结构哈希表,其是为了提高添加和删除元素的速度。
unordered_map
使用hash函数来存储键,似乎键不能相同,否则将会不会插入。
初始化,其每个元素为pair型
unordered_map<int, int> a = { {1,3},{1,2} };
插入
insert({key,value});
insert(pair<key,value>());
返回值:pair<unordered_map<key_t,value_t>::iterator,bool>
若失败,则bool->false,iterator 指向map中想要插入的键的pair
若成功,则bool->true,iterator 指向map中插入的键的pair
查询
find(key);
若无则返回unordered_map.end();
若存在则返回unordered_map<key,value>::operator;
其是指向pair的指针。
随机访问
a[1]=4;
operator[] (key) 来随机访问
擦除
a.earse(key);
返回:size_t 0为失败,1 为成功
函数对象
也称函数符,函数符是可以以函数方式与() 结合使用的任意对象,包括函数名、指向函数的指针和重载()运算符的类对象。
即现在的函数也将其是为对象
template <class _Pr>
bool g(_Pr _Pred,const int & a)
{
return _Pred(a);
}
可以这么编译,将_Pred作为类的对象,其重载了()操作符,在调用g()时,可以将函数名作为参数传递,即函数也被视为一种重载了()操作符的对象。
更有甚者,用类代替函数名的作用,还可以对不同的函数进行传参,不同限制的数的函数,如书708
functional(function.h)
其定义了多个模板类函数对象,是为了解决运算符不能作为函数对象传入函数中,
plus<double> add;
cout << add(1.3, 1.4);
//2.7
运算符与相应的函数符p711
binder1st()
plus<double> add;
binder1st <plus<double>> g(add, 5);
cout << g(6);
其将二元函数add()实例变换为一元函数g(),其中5为二元函数第一个默认参数,而且当且仅当add为自适应函数时才可以实现----虽然也不知道时什么意思。
bind1st()
为简化代码而存在,其作用如上相同,唯一不同的是其直接返回一个一元函数实例。
algorithm
for_each()
void show(const int & a)
{
cout << a << " ";
}
int main()
{
vector <int> old_v = { 1,6,7,8,9,10,};
for_each(old_v.begin(), old_v.end(), show);
return 0;
}
for_each(iterator a,iterator b,f)对于迭代器区间[a,b)调用函数f(*i);
注意for_each()函数不能修改迭代的内容,而范围for循环可以修改。
sort()
vector <int> old_v = {5,2,3,4,5 };
sort(old_v.begin(), old_v.end());
for_each(old_v.begin(), old_v.end(), show);
sort(iterator a,iterator b)对迭代器区间[a,b)进行关于<的排序,注意容器类中的类型要有能够处理operator <()的函数。sort(iterator a,iterator b,_pr _Pred)Pred是一个接受两个迭代器变量解引用操作并且返回一个bool值的比较函数
transform()
constexpr int N = 5;
vector <double> a = { 1,4,9,16,24 };
ostream_iterator <double, char> out(cout, " ");
transform<vector<double>::iterator, ostream_iterator<double,char>,double (*)(double)>(a.begin(), a.end(),out, sqrt);
//1 2 3 4 4.89898
transform()接受四个参数,第一与第二指定了范围,第三指定了输出流,第四指定了一元(处理)函数transform()接受五个参数,#1与#2指定了区间1,#3是区间2的起始位置,#4是输出流,#5是二元函数
replace()
vector<int> vector1 = { 3,2,3,4,5,3,7 };
replace(vector1.begin(), vector1.end(), 3, 0);
for (vector<int>::iterator i = vector1.begin(); i != vector1.end(); i++)
{
cout << *i << " ";
}
replace()接受四个参数, ,第一与第二指定了范围,第三指定了被替换的值,第四指定了替换的值
这个为就地算法replace_copy()为copy版本,与上不同的是,其第三个参数指定了复制的起始迭代器replace_if()为if版本,与原不同的是,其多了最后一个函数参数,用来作为谓词函数
next_permutation
string a = "abc";
sort(a.begin(), a.end());
cout << a << endl;
while (next_permutation(a.begin(), a.end()))
cout << a << endl;
next_permutation() 对区间进行排列
算法
- 非修改式序列操作:如find() ,for_each()
- 修改式序列操作:如transform() rand_shuffle() copy()
- 排序和相关操作:如sort(),set_intersection()等集合操作
- 通用数字运算:内容累计,计算内部乘积
前三组位于< alorithm > 文件 而最后一个位于 < numeric > 文件
xutility
count()
list <int> a = { 2,5,4,3,2,1, };
cout<<count(a.begin(), a.end(), 2);
//2
count() 在指定区间中搜索_val的个数
迭代器
对于在一个数组/链表中查找一个特定的元素,我们可以通过泛型来泛化数据类型,但是我们的算法仍然依赖于特定的类,现在我们希望通过一个设计,将我们的算法完全不仅脱离于数据类型,还脱离于存储的数据结构,这就诞生了迭代器,其就像一个泛化接口,将不论是普通数组存储形式还是以链表存储形式都泛化成一个迭代器来遍历数据。
作为一个迭代器,其至少满足以下条件:
- 对迭代器执行解引用操作 *p
- 将一个迭代器赋值给另一个迭代器 p=q
- 能够比较 如 p==q,p!=q
- 能够遍历容器中的所有元素,可以通过++p,p++来实现
iterator operator++ ();//++p 前缀版本
iterator operator++ (int);//p++ 后缀版本,这里的参数int永远不会用到
作为一种编程风格,最好避免直接使用迭代器,而尽可能的使用STL函数(for_each())来处理细节,
也可以使用C++11基于范围的for循环
- 输入迭代器:能访问容器中所有的值,是单向迭代器,可以递增,不能倒退(单通行,只读算法)
- 输出迭代器:单通行,只写算法
- 正向迭代器:它总是按相同的顺序遍历一系列值,迭代器递增后,仍然可以对前面的迭代器解除引用
- 双向迭代器:拥有两种顺序
- 随机访问迭代器:能够直接跳到容器中的任何一个元素
指针也是迭代器的一种实现,因此
int a[] = { 5,4,3,2,1 };
sort(&a[0], &a[5]);
for (int i = 0; i < 5; i++)
{
cout << a[i] << " ";
}
//1 2 3 4 5
copy(iterator a,iterator b,iterator c)将区间[a,b)复制到c处,以c为开头
int a[] = { 5,4,3,2,1 };
vector <int> b(10);
ostream_iterator <int, char> out_iter(cout, " ");
copy(&a[0], &a[5], b.begin());
copy(b.begin(), b.end(), out_iter);
//5 4 3 2 1 0 0 0 0 0
输出迭代器首先创建一个数组a,再创建一个vector向量b,长度为10,声明一个out_iter输出迭代器,其表示使用cout为输出流,使用” “来作为每次发送给输出流后的分割符,然后程序将a的值复制到b中,再使用out_iter来显示b的值。
ostream_iterator是输出迭代器,使用两个模板参数,第一个为发送给输出流的数据类型,第二个为输出流使用的数据类型。
int a[5] = { 1,2,3,4,5 };
copy(istream_iterator<int, char>(cin), istream_iterator<int, char>(), a);
for (int i = 0; i < 5; i++)
{
cout << a[i] << " ";
}
//<-1 2 3 4 j
//->1 2 3 4 1
输入迭代器典型代表,istream_iterator使用两个模板参数,第一个为要读取的数据类型,即往程序中写的数据类型(目标数据类型),第二个参数为输入流使用的数据类型,函数参数为cin,即为cin管理的输入流,istream_iterator<int, char>()代表输入失败
其他迭代器
vector <int>a = { 1,2,3,4,5 };
copy(a.rbegin(), a.rend(), ostream_iterator<int, char>(cout, " "));
rbegin()为反向迭代器,其实际指向超尾元素
rend()为反向迭代器,其实际指向第一个元素
对于反向迭代器执行++操作,即为正常迭代器的–操作。通过使用反向迭代器,可以复用copy()的含有++代码,并且完成反向打印的操作。
插入迭代器
back_insert_iterator:将元素插入到容器尾部,只允许做快速插入的容器
front_insert_iterator:将元素插入到容器头部,只允许做时间固定的插入的容器类型
insert_iterator:将元素插入到容器某位置,该位置由insert_iterator的构造函数决定
vector <int>a = { 1,2,3,4,5 };
vector <int>b = { 6,7,8,9,10 };
back_insert_iterator<vector<int>> back_iter(a);
copy(b.begin(), b.end(), back_iter);
for (int i : a)
{
cout << i << " ";
}
//->1 2 3 4 5 6 7 8 9 10
back_insert_iterator拥有一个模板参数,将我们的模板传入,让其选择合适的容器方法,同时将容器作为参数传递,使其可以使用类似push_back()的方法,copy()是一个独立的函数,其不能使用容器的一些添加方法,但是通过插入迭代器,使其可以间接的使用。
vector <int>a = { 1,2,3,4,5 };
vector <int>b = { 6,7,8,9,10 };
insert_iterator<vector<int>> insert_iter (a, a.begin()+1);
copy(b.begin(), b.end(),insert_iter);
for (int i : a)
{
cout << i << " ";
}
insert_iterator接受一个模板参数,一个容器类,一个指向容器类特定位置的指针
容器种类
- 容器的对象不能是任意类型,准确的说其类型要具有可复制构造和可赋值的。
其他库
- vector valarray array 三者的区别
- vector 是容器类和算法系统的一部分,其支持容器的操作,如排序,插入、重新排列,搜索,转移容器
- valarray 类模板是面向数值计算的,但是它没有
push_back()和insert() - array 是为了替代内置数组的,它比内置数组更加的安全,它是固定长度的数组,所以不支
push_back()和insert(),但是他有多个STL方法,begin(),end(),rbegin(),rend()
vector <int> ved1;
array<double,10> vod1;
valarray<double> vad1;
initializer_list
vector <int> a{1,2,3,4};//<=>vector <int> a({1,2,3,4});
C++11新增了initializer_list,即用不定长数组初始化
但是也有用{}进行初始化
A a{1,2,3};//<=>A a(1,2,3);
因此,C++ 规定,如果类有initializer_list语法,则对于{}初始化一致认为使{}初始化语法,而不是()的代替
在代码中使用initializer_list初始化语法,可以使用成员函数begin(),end()来访问元素,用size()来返回元素数。
initializer_list为字面量,所以不能修改initializer_list中的值
T g(initializer_list<T> il)
{
T sum = 0;
for (const T* i = il.begin(); i != il.end(); i++)
{
sum += *i;
}
return sum;
}
int main()
{
cout << g<int>({1, 2, 3, 4, 5});
}
//15
2万+

被折叠的 条评论
为什么被折叠?



