vector(向量),C++中的一种容器类,它相当于一个动态的数组。
用法:
文件头 #include<vector>
函数调用:
目录
6. vector 反转、排序、查找位置、元素去重、求交集、并集
1. 创建vector<T>容器
例如,生成存放 double 型元素的 vector<T> 容器:
vector<double> values;
因为容器中没有元素,所以没有分配空间,当添加第一个数据项(如 values.push_back(99)时,会自动分配内存。
创建 vector 容器的另一种方式是使用初始化列表来指定初始值以及元素个数:
vector<int> values {2, 3, 4, 5, 6};
最佳的方式,预先分配一些适当的内存,不够再扩,减少空间额外分配的次数,使用初始元素个数来生成 vector 容器:
vector<double> values(20); // 20 个元素,它们的默认初始值都为 0
vector<long> numbers(20, 99L); // 第二个参数指定了所有元素的初始值
区分两个概念:vector 的容量大小,是指在不分配更多内存的情况下可以保存的最多元素个数,使用
capacity()
函数获得。vector 的大小是它实际所包含的元素个数,也就是有值的元素的个数,调用
size()
函数获得。
可以像下面这样通过调用 reserve() 来增加容器的容量:
vector<int> values {1,2,3}; // values.size() =3, values.capacity() = 3
values.reserve(20); // values.size() =3, values.capacity() = 20
这样就设置了容器的内存分配,至少可以容纳 20 个元素。如果当前的容量已经大于或等于 20 个元素,那么这条语句什么也不做。注意,调用 reserve() 并不会生成任何元素,也不会影响现有的元素。但是,如果通过调用 reserve() 来增加内存,任何现有的迭代器,例如开始迭代器和结束迭代器,都会失效,所以需要重新生成它们。
也可以调用成员函数 resize() 改变容器大小,这也可能导致容量的增加。下面是 resize() 的几种用法:
values.resize (5); // 容量变为参数指定的值values.capacity() = 5,也会增加2个用默认值初始化的元素values.size() =5
values.resize (7, 99); // 增加到第一个参数指定的值values.capacity() = 7,并用第二个参数初始化增加的新元素
2. vector获取(访问)元素
可以在方括号中使用索引,为现有元素设定值,或者只是通过表达式使用它的值。也可以采用at()函数,例如:
values[0] = 9; 等同于 values.at(0) = 9;
values[1] = 2*values[0]; 等同于 values[1] = 2*values.at(0);
特别注意两点:
1 vector 的索引从 0 幵始,这和标准数组一样。通过使用索引,总是可以访问到现有的元素,但是不能这样生成新元素
2 并不会检查索引值,所以当索引可能越界时,例如values[-99]不会报错!!!!应该通过 at() 函数去使用这个元素。
此外还有两个特殊的成员函数 front() 和 back() 分别返回序列中第一个和最后一个元素的引用。因为成员函数 front() 和 back() 返回的是引用,所以它们可以出现在赋值运算符的左边。
cout << values.front() << values.back();
values.front() = 99;
values.back() = 999;
成员函数 data() 返回一个指向数组的指针,它在内部被用来存储元素。
auto pData = values.data();
3. vector添加(增加)元素
通过使用容器对象的 push_back() 函数,在序列的末尾添加一个元素。
values.push_back(99);
还有一个更好的方法来添加元素。emplace_back() 比 push_back() 更有效率。
values.emplace_back(99);
emplace_back() 的参数正是添加到容器中的对象的构造函数所需要的参数。emplace_back() 用它的参数作为构造函数的参数,在容器中生成对象。可以在 emplace_back() 函数中使用尽可能多的参数,只要它们满足对象构造函数的要求。
例如,对于vector<string> words;
words.emplace_back(“abcdef”, 2, 3); // Create string object corresponding to "cde" in place
emplace_back() 函数会调用接收三个参数的 string 构造函数,生成 string 对象,然后把它添加到 words 序列中。构造函数会生成一个从索引 2 幵始、包含“abcdef”中3个字符的子串“cde”。
4. vector插入元素
使用成员函数 emplace(),可以在 vector 序列中插入新的元素。emplace() 的第一个参数是一个迭代器,它指定了被插入对象生成的位置。第一个参数后的参数,都作为插入元素的构造函数的参数传入。
vector<string> words {"1", "4"};
auto iter = words.emplace(++begin(words),"2"); // words={"1", "2", "4"}
words.emplace(++iter, "3"); // words={"1", "2", "3", "4"}
在 emplace() 的第一个参数的后面,可以使用尽可能多的参数,只要它们是被插入对象的构造函数所需要的。例如下面会得到一个由构造函数 string(3,'5') 生成的字符串对象,
words.emplace(end(words), 3, '5'); // words={"1", "2", "3", "4", "555"}
emplace() 会返回一个指向当前插入元素的迭代器iter。
begin()返回指向第一个元素的迭代器。
end()返回指向最后一个元素的后面一位的迭代器。(end(words)-1 才是"555")
rbegin()、rend()相对应表示反向迭代器。(rbegin(words)是"555",rend(words)-1 才是"1")
成员函数 insert() 可以在 vector 中插入一个或多个元素。第一个参数总是一个指向插入点的迭代器。
vector<string> words {"1", "4"};
1) 插入第二个参数指定的单个元素:
auto iter = words.insert(++begin(words), "2"); // words={"1", "2", "4"}
2) 插入一个由第二个和第三个参数指定的元素序列,第二、三个参数也是迭代器:
string more[] {"5", "6", "7"};
iter = words.insert(end(words) , begin(more), end(more)); // words={"1", "2", "4", "5", "6", "7"} 迭代器iter指向第1个元素"5"
3) 在 vector 的末尾插入一个元素:
iter = words.insert(end(words), "8"); // words={"1", "2", "4", "5", "6", "7", "8"}
4) 在插入点插入多个单个元素。第二个参数是第三个参数所指定对象的插入次数:
iter = words.insert(cend(words)-5, 2, "3"); // words={"1", "2", "3", "3", "4", "5", "6", "7", "8"} cend()就是const end()迭代器
5) 在插入点,插入初始化列表指定的元素。第二个参数就是被插入元素的初始化列表:
iter = words.insert(end(words), {"9", "10"}); // words={"1", "2", "3", "3", "4", "5", "6", "7", "8", "9", "10"}
6) 查找vector中是否存在某个元素,并在该元素的前面或后面插入新的元素。
需要利用find()函数,头文件#include <algorithm>
find() 算法会在头两个参数所指定的一段元素中,搜索第三个参数指定的元素,返回第一个找到的元素。它会返回一个迭代器,这个迭代器和用来指定搜索范围的迭代器有相同的类型。
vector<string> words={"1", "2", "4", "6"};
auto riter = find(rbegin(words), rend(words) , "4");
words.insert(riter.base(), "-5"); // words={"1", "2", "4", "-5", "6"}
// words.insert(riter.base()-1, "--5"); // words={"1", "2", "--5", "4", "6"}
使用反向迭代器,会从右向左反向搜索,find()会找到这段元素中最后匹配的元素,如果没有找到匹配的元素,会返回rend(str);
调用 riter 的成员函数 base() 可以得到一个标准迭代器,它指向 riter 右边加一的位置。
auto riter2 = find(begin(words), end(words) , "4");
if(iter !=end(data)){
words.insert(riter2, "+3"); // words={"1", "2", "+3", "4", "6"}
}
使用标准迭代器,会从左向右正向搜索,找到第一个匹配的元素,如果没有匹配的元素,会返回 end(str)。
记住,所有不在 vector 尾部的插入点都会有开销,需要移动插入点后的所有元素,从而为新元素空出位置。当然,如果插入点后的元素个数超出了容量,容器会分配更多的内存,这会增加更多额外开销。
5. vector删除元素
1) 可以通过使用 vector 的成员函数 clear() 来删除所有的元素。例如:
vector<int> data(10, 99); // size---10 capacity---10
data.clear(); // size---0 capacity---10
这个操作并没有改变容器的容量,所以容量还是 10。
2) 可以使用 vector 的成员函数 pop_back() 来删除容器尾部的元素。例如:
data.pop_back(); // size---9 capacity---10
这个操作并没有改变容器的容量,所以容量还是 10。
只要不在意元素的顺序,就可以通过删除最后一个元素的方式来删除容器的任何元素,这不需要移动大量元素。假设要删除 data 中的第二个元素,可以像这样操作:
swap(begin(data)+1, end(data)-1); // 它在头文件 algorithm 和 utility 中都有定义
data.pop_back();
这个函数将第二个元素和最后一个元素互相交换。然后调用 pop_back() 移除最后一个元素,这样就从容器中移除了第二个元素。
注意,vector 也有成员函数 swap(),这个函数用来交换两个 vector 容器中的元素。显然,这两个容器的元素类型必须相同。
全局的 swap() 函数只要将两个容器作为参数,也可以交换它们的元素。
vector<int> data={1,2,3,4,5,6,7,8};
vector<int> data2={9,10};
data.swap(data2);
3) 通过使用成员函数 shrink_to_fit() ,去掉容器中多余的容量,
data.shrink_to_fit();
如果它生效了,那么这个容器现有的迭代器都失效。
4) 使用成员函数 erase() 来删除容器中的一个或多个元素。
如果只删除单个元素,那么只需要提供一个参数,
auto iter = data.erase(begin(data)+1); //Delete the second element
删除一个元素后,vector 的size减 1;但容量不变。会返回一个迭代器,它指向指向被删除元素后的一个元素。
如果要移除一个元素序列,需要传入两个迭代器,用来指定移除元素的范围,
auto iter = data.erase(begin(data)+1, begin(data)+3); // Delete the 2nd and 3rd elements
返回的迭代器指向被删除元素后的位置,它是begin(data)+1 ;如果删除了最后一个元素,它就是end(data)。
5) 删除匹配特定值的一段元素。
remove() 算法由定义在 algorithm 头文件中的模板生成,它可以
vector<string> words { "1", "2","3", "3”, "3", "4","5"};
auto iter = remove(begin(words), end(words), "3"); // iter 指向words最后一个元素之后的位置,如果未找到,则iter指向end(words)
words.erase(iter, end(words)); // 必须与上面一行联合使用,
或等价于 words.erase(remove(begin(words), end(words),"3"), end(words));
在前两个参数指定的元素范围内,移除了所有匹配 remove() 的第三个参数的元素。
移除元素这个表述有一点误导,remove() 是一个全局函数,所以它不能删除容器中的元素。都是通过用匹配元素右边的元素来覆盖匹配元素的方式移除元素。移动覆盖后,iter 指向words最后一个元素“5”之后的位置,接下来需要再删除末尾不想要的元素。
6. vector 反转、排序、查找位置、元素去重、求交集、并集
1) 使用reverse将元素翻转
需要头文件#include <algorithm>
vector<int> data={1,2,3,4,5,5,6,7,8};
reverse(begin(data), end(data)); // data={8, 7, 6, 5, 5, 4, 3, 2, 1}
2) 使用sort排序
需要头文件#include<algorithm>,
vector<int> data={8,2,7,4,5,1};
sort(begin(data), end(data)); // 默认是按升序排列,即从小到大 data={1, 2, 4, 5, 7, 8}
可以通过重写排序比较函数按照降序比较,
bool comp(int &a, int &b){
return a>b;
}
sort(begin(data), end(data)); // 按降序序排列 data={8, 7, 5, 4, 2, 1}
3) 查找某个元素在vector的位置,
需要头文件#include<algorithm>,
auto iter=find(begin(data), end(data),9);
if(iter !=end(data)){ //如果满足不相等的条件,则证明找到了这个元素
auto dis = distance(begin(data), iter);
cout << dis <<endl;
}
4) 元素去重
sort(data.begin(), data.end());
auto iter = unique(data.begin(), data.end());
if (iter != data.end()) {
data.erase(iter, data.end());
}
unique函数的功能是元素去重。即”删除”序列中所有相邻的重复元素(只保留一个)。此处的删除,并不是真的删除,而是指重复元素的位置被不重复的元素给覆盖l。由于它”删除”的是相邻的重复元素,所以在使用unique函数之前,一般都会将目标序列进行排序。
5) 求两个vector的交集、并集
vector<int> data={8,2,2,7,4,2,5,1};
vector<int> data2={8,2,9,3,1};
vector<int> inter_result, unions_result;
sort(begin(data),end(data));
sort(begin(data2), end(data2));
set_intersection(begin(data), end(data), begin(data2), end(data2), inserter(inter_result, begin(inter_result)));
set_union(begin(data), end(data), begin(data2), end(data2), inserter(unions_result, begin(unions_result)));
先把两个vector排序,采用set_intersection求它们的交集,存入inter_result={1,2,8};
采用set_union求它们的并集存入unions_result={1, 2, 2, 2, 3, 4, 5, 7, 8, 9}。特别注意重复的元素2!
参考文献:
http://c.biancheng.net/view/416.html
如有侵权,请联系本人([email protected]),将诚意修改或删除。