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]),將誠意修改或刪除。

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