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