C++ 容器类 vector

 vector(向量),C++中的一种容器类,它相当于一个动态的数组。

用法:

文件头 #include<vector>

函数调用:

目录

1. 创建vector容器

2. vector获取(访问)元素

3. vector添加(增加)元素

4. vector插入元素

5. 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]),将诚意修改或删除。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章