博主从几年前开始脱离ACM大军,至今已经三个年头,花了点时间改善了一下环境。最近回归程序猿大军,发现代码力一落千丈,早已不是以前一晚上横扫OJ的程序猿预备军了。现在已经沦为代码渣渣。至此从新开始写博客,从新巩固代码力。会陆续的把学习的过程更新出来,希望代码力早日恢复T_T。
最近刷LeetCode,才发现原来在刷ACM的时候,用大量的C代替了STL的做法在leetcode上实行起来并不方便,所以痛定思痛。从很久以前不想学的STL开始入手,早就听说STL是个好东西,只是习惯了写C,就懒得去学C++的东西了。既然要踏实的学习,就好好研究,看看能否体会到STL的伟大之处。
博主学习东西比较随性,不会一板一眼的介绍概念什么的,通常都是想到什么就测试什么,然后贴出结论。当然如果有新概念就会进行阐述的。博客的主要目的就是给自己做笔记提醒用。
好啦,正式开始。
Vector,中文是“向量”,多么难懂的名字,向量明明是几何里的概念好吗?之前对vector还是云里雾里的时候,听同学吹牛说用向量做存储,着实的崇拜了一把,其实,这个东西说白了就是数组。不过它有一点特别就是,vector可以根据你的定义,做各种数据类型的数组。比如:
vector<int>
vector<string>
vector<struct a>
vector< vector<int> > //注意最后两个>之间要有空格
例子:
#include <cstdio>
#include <iostream>
#include <vector>
#include <string>
using namespace std;
struct a{
int a;
int b;
char c;
};
void test(){
vector<int> a;
//cout << a.at(100) << endl; // When the index is greater than the range, it would show error.
for (int i = 0; i < 100; i ++){
a.push_back(i);
cout << a[i];
}
cout << endl;
vector<int> b;
for (int i = 1; i < 100; i ++){
b.push_back(i + 1);
}
cout << (a == b) << endl; // Tow vector
struct a bb;
bb.a = 1;
bb.b = 2;
bb.c = 'c';
vector<struct a> strctv;
strctv.push_back(bb);
cout << (strctv[0].a == strctv[1].b);
string str1, str2;
str1 = "abcd";
str2 = "zyxw";
vector<string> vs;
vs.push_back(str1);
vs.push_back(str2);
cout << vs[0] << vs[1] << endl;
vector<vector<int> > vv;
vv.push_back(a);
vv.push_back(b);
cout << vv[0][0] << vv[1][1] << endl;
}
int main(void){
test();
return 0;
}
简单的赋值和输出代码,展示vector可以装各种类型。以及vector变量之间可以进行 ==, !=, <=, >=, <, > 操作。但是如果vector包含的是结构体,需要重载这些操作符。
有一点需要注意,vector 可以作为几乎所有类型的容器,但是单个容器中的类型只能有一种。比如博主本来想要做vector<vector>,结果无情的被报错了。
知道了如何定义之后,我们来根据上面的例子说一下几个简单的操作,赋值、调用,状态。
赋值:
1, assign(int num, TYPE val);assign(input_iterator start, input_iterator end);
在定义某个vector之后或者是使用了一段时间后,清空之前的vector内容,利用assign重新赋值num个类型是TYPE的val。
vector<int> vi;
vi.assign(12, 1);
//这时,vi的内容就是{1,1,1,1,1...,1}
同样的还有另一种形式,如下,可以完成这个效果,结果一样。
vector<int> vi2(12, 1);
所以,assign是一个很好的初始化使用的函数,只是因为只是赋值同样的东西,所以可能并不是特别实用。
assign函数还有另一个重载assign(input_iterator start, input_iterator end),就是说,用另一个vector的iterator取某一个vector的一段赋值给另一个vector。
给一个例子:
void test1(){
vector<int> vi;
for (int i = 0; i < 12; i++){
vi.push_back(i);
}
for (int i = 0; i < 12; i++){
cout << vi[i];
}
cout << endl;
std::vector<int>::iterator it;
it = vi.begin() + 3;
vector<int> vi2;
vi2.assign(it, vi.end() - 3);
std::vector<int>::iterator it2;
for (it2 = vi2.begin(); it2 != vi2.end(); it2++){
cout << *it2;
}
cout << endl;
<span style="white-space:pre"> </span>vi.assign(vi.begin() + 3, vi.end() - 3);
<span style="white-space:pre"> </span>for (it2 = vi2.begin(); it2 != vi2.end(); it2++){
<span style="white-space:pre"> </span>cout << *it2;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>cout << endl;
}
输出结果是:
01234567891011
345678
345678
首尾各3个值被拿掉,因为迭代器it = vi.begin() +3,而assign的第二个参数也是vi.end() - 3。这就表示assign的迭代器使用很灵活,可以直接通过vector实例的begin(),end()等作为参数,也可以通过迭代器变量作为参数。 大家注意一下第三个输出,这个是vi使用assign作用于自己。也就是说,可以用assign()不断的缩小vector自身。
其实在定义vector的时候,vector的构造函数可以完成和assign几乎相同的功能。主要形式有如下四种:
(1)vector()
(2)vector(size_type n, const TYPE &v);
(3)vector(const vector &from)
(4)vector (input_iterator start, input_iterator end);
vector<int> first; // 空向量
vector<int> second (12, 1); // 12个1
vector<int> third (second.begin(),second.end()); // 通过迭代器得到和second一样的third
vector<int> fourth (third); // 直接用third初始化fourth
2, push_back(), pop_back()。
这两个是配对的,所以一起说,void push_back(const TYPE val)是在vector的最后添加一个类型为TYPE的元素,值为val(TYPE必须是vector包含的类型)。而void pop_back()是删除掉vector最后的一个元素,注意这个地方没有返回值。
例子:
void test2(){
vector<int> vi;
std::vector<int>::iterator it;
for (int i = 0; i < 12; i++){
vi.push_back(i);
}
for (it = vi.begin(); it != vi.end(); it++){
cout << *it;
}
cout << endl;
for (int i = 0; i < 10; i++){
vi.pop_back();
}
for (it = vi.begin(); it != vi.end(); it++){
cout << *it;
}
cout << endl;
}
输出结果:
01234567891011
01
push_back()了12次,pop_back()了10次,所以最后剩下两个值。
3. insert()
insert()有三种主要的形式:(1)iterator insert (iteartor loc, const TYPE &val); (2) void insert(iterator loc, size_type num, cost_TYPE &val); (3) void insert (iterator loc, input_iterator start, input_iterator end);
(1) 在loc迭代器所在的位置,插入单个值val,并且返回这个值所在的位置的迭代器。
(2) 在loc位置插入num个TYPE类型的值val;
(3) 在loc位置,插入迭代器start到end之间的值。
例子:
void test3(){
vector<int> vi;
std::vector<int>::iterator it;
for (int i = 0; i < 12; i++){
vi.push_back(i);
}
for (it = vi.begin(); it != vi.end(); it++){
cout << *it << " ";
}
cout << endl;
cout << *vi.insert(vi.begin(), 100) << endl;;
vi.insert(vi.begin() + 10, 4, 200);
for (it = vi.begin(); it != vi.end(); it++){
cout << *it << " " ;
}
cout << endl;
vi.insert(vi.begin(), vi.begin(), vi.end());
for (it = vi.begin(); it != vi.end(); it++){
cout << *it << " " ;
}
cout << endl;
}
输出结果:
0 1 2 3 4 5 6 7 8 9 10 11
100
100 0 1 2 3 4 5 6 7 8 200 200 200 200 9 10 11
100 0 1 2 3 4 5 6 7 8 200 200 200 200 9 10 11 100 0 1 2 3 4 5 6 7 8 200 200 200 200 9 10 11
貌似很好理解。
调用:
vector的调用貌似很好用,如同最开所说vector其实就是一个数组,所以可以直接用数组的调用形式来做。
例如:a[0], a[1], a[2],这样。
at(int index):
还有一种形式是使用at(int index)函数,直接访问vector的index小标中的内容。
所以a[0] 等价于 a.at(0).区别在于,如果越界,at()函数会返回错误,[0]则不会返回错误。
(1)front(), back()
这是两个返回值的函数,分别返回第一个和最后一个vector内的元素的引用。
例子:
void test4(){
vector<int> vi(10, 1);
std::vector<int>::iterator it;
int i = 0;
for (it = vi.begin(); it != vi.end(); it ++){
*it = i++;
cout << *it << " ";
}
cout << endl;
cout << "front: " << vi.front() << endl;
cout << "back: " << vi.back() << endl;
vi.front() += 10;
vi.back() -= 10;
for (it = vi.begin(); it != vi.end(); it ++){
cout << *it << " ";
}
cout << endl;
}
输出结果:
void test4(){
vector<int> vi(10, 1);
std::vector<int>::iterator it;
int i = 0;
for (it = vi.begin(); it != vi.end(); it ++){
*it = i++;
cout << *it << " ";
}
cout << endl;
cout << "front: " << vi.front() << endl;
cout << "back: " << vi.back() << endl;
vi.front() += 10;
vi.back() -= 10;
for (it = vi.begin(); it != vi.end(); it ++){
cout << *it << " ";
}
cout << endl;
}
迭代器是我自从接触STL之后一直很头痛的东西,因为名字太长了= =。。。没想到今天在整理前面的内容的时候,渐渐地已经把iterator的用法搞通了。
iterator其实你可以认为是一种遍历的工具。它有点像指针,指定在容器中的某一个位置,通过++遍历下一个。是一个很好用的工具,在STL中有不少函数返回值和参数也是用迭代器类型,方便我们操作。所以我会带着基本的迭代器函数一起介绍。
定义:std::vector<int>::iterator it; //名字很长,但不难记,std是namespace;vector<int>是迭代器所对应容器的类型,和它要操作的容器类型一定是相同的;iterator是固定的迭代器的英文;it是迭代器的变量名。
(2.1) begin(), end():
这两个函数会返回vector的开头元素所在位置和结尾元素所在位置的迭代器。
给个例子:
<span style="white-space:pre"> </span>vector<int> vi(10, 1);
std::vector<int>::iterator it;
int i = 0;
for (it = vi.begin(); it != vi.end(); it ++){
*it = i++;
cout << *it << " ";
}
相对应的有这另一对函数rbegin(), rend(): 他们会返回一个逆向的迭代器,rbegin()指向链表的末尾,rend()指向链表的开头。所以我们可以反向给vector赋值。
给一个例子:
<pre name="code" class="cpp">void test5(){
vector<int> vi(12, 1), rvi(12, 1);
std::vector<int>::reverse_iterator rit;
std::vector<int>::iterator it;
int i;
for (i = 1, it = vi.begin(); it != vi.end(); it++){
*it = i++;
}
for (i = 1, rit = rvi.rbegin(); rit != rvi.rend(); rit ++){
*rit = i++;
}
for (int i = 0; i < 12; i++){
cout << vi[i] << " " ;
}
cout << endl;
for (int i = 0; i < 12; i++){
cout << rvi[i] << " " ;
}
cout << endl;
}
输出结果为:
<pre name="code" class="cpp">1 2 3 4 5 6 7 8 9 10 11 12
12 11 10 9 8 7 6 5 4 3 2 1
(2.2) erase():
删除指定元素的函数。这个函数有两种形式:
iterator erase(iterator loc); //删除掉迭代器loc所指向的元素并返回后一个元素的迭代器
iterator erase(iterator start, iterator end); //删除[start, end)区间内的迭代器,并返回end的迭代器。
例子:
<pre name="code" class="cpp">void test6(){
vector<int> vi(12, 1);
std::vector<int>::iterator it;
int i;
for (i = 1, it = vi.begin(); it != vi.end(); it++){
*it = i++;
}
cout << "Original:";
for (it = vi.begin(); it != vi.end(); it ++){
cout << *it << " " ;
}
cout << endl;
cout << "Delete: " << *(vi.begin() + 3) << endl;
vi.erase(vi.begin() + 3);
cout << "Erase1 Result: ";
for (it = vi.begin(); it != vi.end(); it ++){
cout << *it << " " ;
}
cout << endl;
cout << "Delete from " << *(vi.begin() + 1) << " until " << *(vi.end() - 1) << endl;
cout << "Stop at: " << *vi.erase(vi.begin() + 1, vi.end() - 1) << endl;
cout << "Erase2 Result: ";
for (it = vi.begin(); it != vi.end(); it ++){
cout << *it << " " ;
}
cout << endl;
}
输出:
Original:1 2 3 4 5 6 7 8 9 10 11 12
Delete: 4
Erase1 Result: 1 2 3 5 6 7 8 9 10 11 12
Delete from 2 until 12
Stop at: 12
Erase2 Result: 1 12
状态:
(1)vector的容量相关:capacity(), size(), resize(), reserve(),
capacity(void)是返回vector当前容量的函数。也就是说当前系统给vector分配的空间能够存储多大的容量。
reserve(type_size size) 是设定向量容量为size的函数。在没有给vector设定容量的时候,通常会根据放入的元素数量,由0, 1, 2, 4, 8, 16, 32, 64, 128,256... 这样倍数增长。例如,如果你用push_back()逐个给vector变量添加元素,这个变量的容量会在
插入第一个元素的时候变成1,
插入第二个元素的时候变成2,
插入第三个元素的时候变成4,
插入第五个元素的时候变成8,
插入第九个元素的时候变成16,
插入第十七个元素的时候变成32,
。。。。。。
但是如果你事先知道你可能只要插入50个元素,那么就直接用reserver(50), 之后除非元素总数超过50,否则向量的容量大小是不会变的。
// vector::reserve
#include <iostream>
#include <vector>
int main ()
{
std::vector<int>::size_type sz;
std::vector<int> foo;
sz = foo.capacity();
std::cout << "making foo grow:\n";
for (int i=0; i<100; ++i) {
foo.push_back(i);
if (sz!=foo.capacity()) {
sz = foo.capacity();
std::cout << "capacity changed: " << sz << '\n';
}
}
std::vector<int> bar;
sz = bar.capacity();
bar.reserve(100); // this is the only difference with foo above
std::cout << "making bar grow:\n";
for (int i=0; i<100; ++i) {
bar.push_back(i);
if (sz!=bar.capacity()) {
sz = bar.capacity();
std::cout << "capacity changed: " << sz << '\n';
}
}
return 0;
}
输出是
making foo grow:
capacity changed: 1
capacity changed: 2
capacity changed: 4
capacity changed: 8
capacity changed: 16
capacity changed: 32
capacity changed: 64
capacity changed: 128
making bar grow:
capacity changed: 100
size()是返回当前vector内的元素个数,而resize就是重新定义这个个数。
size_type size(void)用法简单,直接调用返回元素个数。
resize()有几种变化,其函数形式为:void resize(size_type n, TYPE val = 0). n是将vector重置的目标数,而val是一个缺省参数,起默认为0.
当 n < size(), 则保留前n个元素;当n > size(), 则在vector末尾添加(n - size())个值为val的元素。
例子:
void test7(){
vector<int> vi(1, 1);
vector<int>::iterator it;
cout << "1: Original size:" << vi.size() << endl;
for (it = vi.begin(); it != vi.end(); it++){
cout << *it << " ";
}
cout << endl;
vi.resize(2, 2);
cout << "2: New size:" << vi.size() << endl;
for (it = vi.begin(); it != vi.end(); it++){
cout << *it << " ";
}
cout << endl;
vi.resize(3, 3);
cout << "3: New size:" << vi.size() << endl;
for (it = vi.begin(); it != vi.end(); it++){
cout << *it << " ";
}
cout << endl;
vi.resize(4, 4);
cout << "4: New size:" << vi.size() << endl;
for (it = vi.begin(); it != vi.end(); it++){
cout << *it << " ";
}
cout << endl;
vi.resize(1);
cout << "5: End size:" << vi.size() << endl;
for (it = vi.begin(); it != vi.end(); it++){
cout << *it << " ";
}
cout << endl;
}
输出结果为:
1: Original size:1
1
2: New size:2
1 2
3: New size:3
1 2 3
4: New size:4
1 2 3 4
5: End size:1
1
(2)swap()函数
swap(vector &from)函数就是当前向量和from向量交换所有的元素,无论size()各是多少,全部交换。相当于交换了指针内容。算是个非常简单的函数。
例子:
void test8(){
vector<int> vi1(3, 3);
vector<int> vi2;
vector<int>::iterator it;
for (int i = 0; i < 12; i++){
vi2.push_back(i + 1);
}
cout << "Original: " << endl;
cout << "vi1: " << endl;
for (it = vi1.begin(); it != vi1.end(); it++){
cout << *it << " ";
}
cout << endl;
cout << "vi2: " << endl;
for (it = vi2.begin(); it != vi2.end(); it++){
cout << *it << " ";
}
cout << endl;
cout << "Swap:vi1.swap(vi2); " << endl;
vi1.swap(vi2);
cout << "vi1: " << endl;
for (it = vi1.begin(); it != vi1.end(); it++){
cout << *it << " ";
}
cout << endl;
cout << "vi2: " << endl;
for (it = vi2.begin(); it != vi2.end(); it++){
cout << *it << " ";
}
cout << endl;
}
输出结果:
Original:
vi1:
3 3 3
vi2:
1 2 3 4 5 6 7 8 9 10 11 12
Swap:vi1.swap(vi2);
vi1:
1 2 3 4 5 6 7 8 9 10 11 12
vi2:
3 3 3
(3) clear(), empty()
这两个函数用法和使用都不相同,放在一起说是因为他们两个的英文意义很像。而且是最后两个需要说明的函数了。
void clear(void)是清空vector内所有的元素。
bool empty(void)是返回vector是否为空,若是则返回true,若不是则返回false。
很简单,不提供例子了。
vector是我第一个研究的STL,感觉通过这次踏踏实实的学习,对STL的用法和认识有了很全面的提高。不知道用vector的函数是否可以类推其他的STL容器的用法,应该可以为以后学习其他的STL节省不少时间。 STL我已经纠结了好几年了,虽然知道是简单的东西,虽然其实涉及到的语法和变化并不多,但是一直没有用心的去学习,每次用到都是去baidu相关用法,和查字典一般。感觉这样并不好,反而事倍功半。与其每次都查,不如一次性搞懂更加有效率。