本篇中使用的調試函數如下
1 template<typename T> 2 void print_vector(vector<T> a){ 3 if(a.size() == 0) 4 cout << "empty" << endl; 5 else{ 6 for(int i = 0; i < a.size(); i++) 7 cout << a[i] << " "; 8 cout << endl; 9 } 10 }
1.vector的初始化
1 vector<double> a; // 生成空向量 2 vector<double> b(5); // 指定大小,每個元素都是初始值0 3 vector<double> c{1,2,3,4,5}; // 指定每一個元素的值 4 vector<double> d(5,3); // 指定大小和統一初始化值 5 vector<double> e(c); // 復制構造函數 6 vector<double> f(begin(e), begin(e) + 3); // 部分復制構造 7 8 print_vector(a); // empty 9 print_vector(b); // 0 0 0 0 0 10 print_vector(c); // 1 2 3 4 5 11 print_vector(d); // 3 3 3 3 3 12 print_vector(e); // 1 2 3 4 5 13 print_vector(f); // 1 2 3
2.向vector中增加(或者是插入)元素
1 vector<int> a; 2 vector<int> b{4,5,6}; 3 4 a.push_back(1); //在末端增加1個元素 5 print_vector(a); // 1 6 7 a.emplace_back(2);//同在末端增加1個元素,比push_back()效率更高 8 print_vector(a); // 1 2 9 10 a.insert(begin(a) + 1, 3);//在迭代器指定的位置插入單個元素 11 print_vector(a); // 1 3 2 12 13 a.insert(end(a),begin(b),end(b));//三個參數都是迭代器,向第一個參數指定的位置中插入從第二個參數到第三個參數中的元素 14 print_vector(a); // 1 3 2 4 5 6 15 16 a.insert(end(a) - 1, 2, 7);//往第一個參數(迭代器)指定的位置插入以第二個參數為個數的第三個參數 17 print_vector(a); // 1 3 2 4 5 7 7 6 18 19 a.insert(end(a) - 2, {8,9});//第一個參數是插入位置,第二個參數是插入的參數(初始化列表形式) 20 print_vector(a); // 1 3 2 4 5 7 8 9 7 6
3.向vector中刪除某些元素
在這里暫時先不介紹remove,對於單純想要刪除元素,remove是不被建議的做法。還有pop_back()用於刪除最后一個元素
1 vector<int> a{1,2,3,4,5,6,7,8,9}; 2 3 a.pop_back(); // 刪除vector中最后的單個元素 4 print_vector(a); // 1 2 3 4 5 6 7 8 5 6 a.erase(a.begin() + 1); // 刪除由迭代器指定的位置中的單個元素 7 print_vector(a); // 1 3 4 5 6 7 8 8 9 a.erase(a.begin() + 2, a.begin() + 4); // 刪除兩個迭代器之間的元素 10 print_vector(a); // 1 3 6 7 8
4.查找
vector支持隨機訪問,可以用方括號運算符實現按下標查找。
按值查找主要用到algorithm中的find()和find_if()兩個算法:
find()函數有三個參數,第一二個參數都是迭代器,分別指向要查找的區間的起始點和終止點,第三個參數是一個特定的值,說明要查找的元素。如果找到則返回一個指向第一個符合條件的元素的迭代器,如果沒有找到則返回指向查找區間終點的迭代器(也就是和第二個參數一樣)。find()函數不僅可以查找基本數據類型的vector,也可以查找自定義的類的vector,要求是自定義的類中重載了==運算符。
find_if()可以實現更廣義的查找。find_if()前兩個參數和find()一樣,第三個參數是一個函數func,func只有一個參數,參數類型是被查找的元素的類型,返回值是bool類型,find_if()會返回使得func返回值為真的元素的迭代器。如果找不到則返回指向查找區間末端的迭代器。
最后是一個小技巧:使用find()或find_if()查找到的指向某個元素的迭代器,減去指向該vector的頭部的迭代器(也就是begin()),可以得到該元素的下標,也就是按值查找下標功能。
1 class Student{ 2 public: 3 int year; 4 int grade; 5 Student(int y, int g):year(y), grade(g){} 6 bool operator==(const Student & Other){ 7 return (year == Other.year); 8 } 9 }; 10 11 bool excellent(Student s){ 12 return s.grade > 85; 13 } 14 15 int main(){ 16 vector<Student> a{Student(17,80), Student(16,95), Student(18,70)}; 17 18 cout << "year: " << a[2].year << " grade: " << a[2].grade << endl; // 根據下標查找值 19 20 vector<Student>::iterator b = find(begin(a), end(a), Student(18,100)); 21 cout << b - begin(a) << endl;/*利用find的結果可以做到根據值查找下標*/ 22 23 auto d = find_if(begin(a), end(a), excellent); 24 cout << d - begin(a)<< endl; 25 }
在這個例子中,find()函數查找的判斷依據是Student類中的==運算符(例子中只要求找到第一個年齡等於18歲的學生),而find_if()中查找判斷的依據是用戶自己定義的excellent函數(例子中是要求找到第一個成績大於85分的學生)
最后,由於find_if()的判斷是自定義的函數,實際上有更多更靈活的寫法,比如重載類的()操作符成為成員函數,或者使用lambda表達式等。
5.修改
C++似乎並沒有python那種直接指把一個指定區間的元素整個替換成另一個區間的元素的操作。由於vector的實現是動態數組,往數組中插入或者刪除元素的時間復雜度都是O(n),如無必要不要經常反復增刪vector元素.assign有兩種使用方式,但是這兩種使用方式都會直接清除這個向量之前內部的所有數據。隨機訪問還可以用at,作用一樣。
1 vector<int> a{1,2,3}; 2 vector<int> b{4,5,6,7}; 3 a[1] = 0; 4 print_vector(a); // 1 0 3 5 6 a.assign(begin(b), end(b)); 7 print_vector(a); // 4 5 6 7 8 9 a.assign(5,2); 10 print_vector(a); // 2 2 2 2 2
6.排序
使用vector內自帶的排序功能排序,對兩個迭代器內指定的區間按照自定義的方式進行排序,cmp參數是兩個待比較的值類型,返回值是bool類型
1 class A{ 2 public: 3 int n, m; 4 A(int N, int M):n(N), m(M){} 5 }; 6 7 bool cmp(A p1, A p2){ 8 return (p1.m > p2.m); 9 } 10 11 int main(){ 12 vector<A> a{A(1,4),A(3,3),A(2,5),A(5,1),A(4,2)}; 13 sort(a.begin(), a.end(), cmp); // 按照m從大到小排序 14 }
7.迭代器
迭代器本質上就是指向元素的指針,跟指針的用法差不多,對於指向同一個容器的兩個迭代器iter1和iter2,可以使用自加/自減或者加減某一個整數的操作使得迭代器左右移動。也可以用比較運算符==和!=(只有vector和queue的迭代器才可以使用<和>比較)。同時還可以相減得出下標的差值。由於迭代器相當於指針,因此可以使用*解引用或者如果是類可以使用->取其成員變量。
特殊的迭代器有begin和end,以及rbegin和rend可以用來初始化迭代器。
注意區分front,back和being,end,前者的兩個分別返回的是第一個元素和最后一個元素的引用。
1 vector<double> a{1.1, 2.2, 3.3, 4.4, 5.5}; 2 vector<double>::iterator iter1 = a.begin(); 3 auto mid = iter1 + a.size() / 2; 4 cout << *mid << endl;
8.其他
size()獲取大小,reverse()翻轉,clear()清除所有元素,swap()交換元素,resize()改變大小(如果大於現大小則往后補默認元素,小於現大小則在尾端刪除至要求的大小),capacity()獲取容量(當vector大小達到容量上限時會啟動自動擴容,但是自動擴容是一件很耗時間的事情,而且自動擴容期間會導致現有的迭代器和指針失效。所以實現估計大小並提前申請好容量大小是必要的。),reserve()手動擴容,count()統計某個元素出現次數。
最后重點說明兩個:
1.remove()可以實現類似於“刪除”某個元素的功能,以數組b[2,3,4,3,5]為例,如果想remove(begin(b),end(b),3),將會執行以下幾步
申請兩個指針i,j都指向b[0]
*i 不等於3(也就是b[0]),把i指向元素的值賦給j指向元素的值(b[0] <- b[0])++i,++j
*i 等於3(也就是b[1]),++i,j不動(j相當於標記了一個坑的位置)
*i 不等於3(也即是b[2]),把i指向元素的值賦給j指向元素的值(b[1] <- b[3])++i,++j
*i 等於3(也就是b[3]),++i,j不動
*i 不等於3(也即是b[4]),把i指向元素的值賦給j指向元素的值(b[2] <- b[5])++i,++j
最后的結果就是[2,4,5,3,5],從過程可以看出,是吧所有非3的元素都往前移,把原來是3的位置補上,而后面多出來的位置保持不變。remove返回值是就是上文中的j。
因此,想要用remove真正刪除元素3,應該寫成:
1 b.erase(remove(begin(b), end(b),3), end(b));
2.去重:想要去除vector中的重復元素有兩種思路:
一是把vector轉換成set再轉換成vector
二是sort+unique+erase(unique的作用是把所有相鄰且重復的元素放到列表末尾)
1 vector<int> a{1,2,3,4,5,6,7,8,9}; 2 cout << a.size() << endl; // 輸出9 3 4 reverse(begin(a),end(a)); // reverse是algorithm中的函數,用於翻轉 5 print_vector(a); // 9 8 7 6 5 4 3 2 1 6 7 remove(begin(a), end(a), 5); 8 print_vector(a); // 9 8 7 6 4 3 2 1 1 9 10 reverse(begin(a), end(a)); 11 print_vector(a); // 1 1 2 3 4 6 7 8 9 12 13 swap(a[0], a[2]); 14 print_vector(a); // 2 1 1 3 4 6 7 8 9 15 16 a.resize(12); 17 print_vector(a); // 2 1 1 3 4 6 7 8 9 0 0 0 18 19 a.resize(5); 20 print_vector(a); // 2 1 1 3 4 21 22 cout << "Capacity: " << a.capacity() << endl; // 18 23 a.reserve(30); 24 cout << "Capacity: " << a.capacity() << endl; // 30 25 26 cout << "Num of 1: " << count(begin(a), end(a), 1) << endl; // 2 27 28 a.clear(); 29 cout << a.empty() << endl; // 輸出1,即true