本文參考文獻::GeekBand課堂內容,授課老師:張文傑html
:C++ Primer 11 中文版(第五版)ios
:網絡資料: 葉卡同窗的部落格 http://www.leavesite.com/算法
http://blog.sina.com.cn/s/blog_a2a6dd380102w73e.htmlwindows
一、什麼是vector?數組
簡單的理解:數組!網絡
進一步的理解:變長一維的動態數組,連續存放的內存塊,堆內分配內存。支持[]操做(一會就會用到),支持下標操做。數據結構
再進一步的理解:vector表示對象的集合,集合中每一個對象都有與之對應的索引(理解爲下標),這裏能夠保存不少元素的對象,包括不限於,如int string、或者本身定義的類的對象等等。app
二、Vector的初始化less
有幾種基本初始化方法:函數
vector<T> v1 ; //構造函數 vector<T> v2(v1) ; //拷貝構造函數 vector<T> v2 =v1 ; //拷貝賦值 vector<T> v1{ 0, 0, 30, 20, 0, 0, 0, 0, 10, 0 }; //初始化
本文中採用最基本的方法進行初始化。
三、Vector基本操做
vector<int> vec; vector<int> vec2; vec.push_back(t); //向v的尾端添加元素t vec.empty(); vec.size(); vec == vec2; //相等相似的操做都有
4、迭代器
簡單的理解:相似於指針。以下面的一句話,迭代器有一個優勢,那就是全部的標準庫容器均可以使用迭代器,具備極強的通用性。
vector<int>::iterator result = find_if(Vec1.begin(), Vec1.end(), bind2nd(not_equal_to<int>(), unexpectedInt));
5、本文中可能會用到的一些(算法)操做:
一、函數 :not_equal_to,重載了操做符(),判斷左右兩個數是否相等,不等返回值 TRUE、即1,相等返回 FALSE、即0.
相似的函數有equal_to
template<class _Ty = void> struct not_equal_to : public binary_function<_Ty, _Ty, bool> { // functor for operator!= bool operator()(const _Ty& _Left, const _Ty& _Right) const { // apply operator!= to operands return (_Left != _Right); } };
二、bind1st 和 bind2nd
bind1st(const Fn2& Func,const Ty& left) :1st指:傳進來的參數應該放左邊,也就是第一位 bind2nd(const Fn2& Func,const Ty& right) :2nd指:傳進來的參數應該放右邊,也就是第二位
簡單的兩個例子:
#include "stdafx.h" #include <vector> #include <functional> #include <algorithm> #include <iostream> #include <iterator> #include<windows.h> #include <Mmsystem.h> using namespace std; void ShowArray(vector<int> &Vec) { vector<int>::iterator it = Vec.begin(); //it 是一個地址 while (it < Vec.end()) { cout << *it << endl; it++; } }; int _tmain(int argc, _TCHAR* argv[]) { int a[] = { 1, 2, 100, 200 }; std::vector<int> arr(a, a + 4); // 移除全部小於100的元素 arr.erase(std::remove_if(arr.begin(), arr.end(), std::bind2nd(std::less< int>(), 100)), arr.end()); ShowArray(arr); /**************************************/ printf("*******************************\n"); int b[] = { 1, 2, 100, 200 }; std::vector<int> arr2(b, b + 4); // 移除全部大於100的元素 arr2.erase(std::remove_if(arr2.begin(), arr2.end(), std::bind1st(std::less< int>(), 99)), arr2.end()); ShowArray(arr2); }
本例中由於僅判斷是否爲0 ,全部採用bind1st 和 bind2nd都同樣。
三、remove_copy_if
remove_copy_if() 函數原型
template<class _InIt,class _OutIt,class _Pr> inline _OutIt remove_copy_if(_InIt _First, _InIt _Last, _OutIt _Dest, _Pr _Pred) { // copy omitting each element satisfying _Pred _DEBUG_RANGE(_First, _Last); _DEBUG_POINTER(_Dest); _DEBUG_POINTER(_Pred); return (_Remove_copy_if(_Unchecked(_First), _Unchecked(_Last), _Dest, _Pred, _Is_checked(_Dest))); }
remove_copy_if()的思考方式和copy_if()相反,若IsNotZero為true,則不copy,若為false,則copy。
remove_copy_if(Vec1.begin(), Vec1.end(), back_inserter(Vec2), IsNotZero);
此時要求: 當unexpectedInt 爲0時,返回值爲TRUE,不進行拷貝;當unexpectedInt 不爲0時,返回值爲FALSE,則進行copy。
bool IsNotZero(int unexpectedInt) { return (unexpectedInt == 0); }
完成代碼以下: 開發環境 VS2013 IDE
// Vector.cpp : 定義控制檯應用程序的入口點。 // /* 問題: 給定一個 vector:v1 = [0, 0, 30, 20, 0, 0, 0, 0, 10, 0], 但願經過 not_equal_to 算法找到到不爲零的元素,並複製到另外一個 vector: v2 */ /* 要點一: 第一步、利用not_equal_to函數進行數值比較,區分vector某一元素是不是非0數據 第二步、查找全部的非0元素 第三步、將全部非0元素拷貝到v2中來 要點二:效率問題 測試結果: 利用下標耗費時間最少,運行速度比較快,但不通用(vector能夠利用下標)。 利用迭代器耗費時間較多,但更爲通用。 利用C++ 11 remove_copy_if() algorithm 進行分析 總結: C++ 11 remove_copy_if() algorithm 代碼最少,效率最高。 */ #include "stdafx.h" #include <vector> #include <functional> #include <algorithm> #include <iostream> #include <iterator> #include<windows.h> #include <Mmsystem.h> using namespace std; #pragma comment( lib,"winmm.lib" ) //利用下標的方法 void FiltArray0(vector<int> &Vec1, vector<int> &Vec2, const int unexpectedInt) { //測試時間 LARGE_INTEGER litmp; LONGLONG qt1, qt2; double dft, dff, dfm; QueryPerformanceFrequency(&litmp);//得到時鐘頻率 dff = (double)litmp.QuadPart; QueryPerformanceCounter(&litmp);//得到初始值 //測試時間開始 qt1 = litmp.QuadPart; int size = Vec1.size(); for (int i = 0; i<size; i++) { if (not_equal_to<int>()(Vec1[i], unexpectedInt)) { Vec2.push_back(Vec1[i]); } else continue; } QueryPerformanceCounter(&litmp);//得到終止值 qt2 = litmp.QuadPart; dfm = (double)(qt2 - qt1); dft = dfm / dff;//得到對應的時間值 cout<<"下標方法測試時間爲:" << dft << endl; } //使用迭代器 void FiltArray1(vector<int> &Vec1, vector<int> &Vec2, const int unexpectedInt) { //測試時間 LARGE_INTEGER litmp; LONGLONG qt1, qt2; double dft, dff, dfm; QueryPerformanceFrequency(&litmp);//得到時鐘頻率 dff = (double)litmp.QuadPart; QueryPerformanceCounter(&litmp);//得到初始值 //測試時間開始 qt1 = litmp.QuadPart; //查找第一個不爲0的數值 vector<int>::iterator result = find_if(Vec1.begin(), Vec1.end(), bind2nd(not_equal_to<int>(), unexpectedInt)); while (result != Vec1.end()) { Vec2.push_back(*result); //result結果的下一位開始查找不爲0的數 result = find_if(result + 1, Vec1.end(), bind2nd(not_equal_to<int>(), unexpectedInt)); } QueryPerformanceCounter(&litmp);//得到終止值 qt2 = litmp.QuadPart; dfm = (double)(qt2 - qt1); dft = dfm / dff;//得到對應的時間值 cout << "迭代器方法測試時間爲:" << dft << endl; } bool IsNotZero(int unexpectedInt) { return (unexpectedInt == 0); } void FiltArray2(vector<int> &Vec1, vector<int> &Vec2, const int unexpectedInt) { //測試時間 LARGE_INTEGER litmp; LONGLONG qt1, qt2; double dft, dff, dfm; QueryPerformanceFrequency(&litmp);//得到時鐘頻率 dff = (double)litmp.QuadPart; QueryPerformanceCounter(&litmp);//得到初始值 //測試時間開始 qt1 = litmp.QuadPart; // C++ 11 裏的函數 //《effective STL》 :儘可能用算法替代手寫循環;查找少不了循環遍歷 remove_copy_if(Vec1.begin(), Vec1.end(), back_inserter(Vec2), IsNotZero); QueryPerformanceCounter(&litmp);//得到終止值 qt2 = litmp.QuadPart; dfm = (double)(qt2 - qt1); dft = dfm / dff;//得到對應的時間值 cout << "利用拷貝算法測試時間爲:" << dft << endl; } void ShowArray(vector<int> &Vec) { vector<int>::iterator it = Vec.begin(); //it 是一個地址 while (it < Vec.end()) { cout << *it << endl; it++; } } void ClearArray(vector<int> &Vec) { vector<int>::iterator it = Vec.begin(); //清空數據 while (it < Vec.end()) { it = Vec.erase(it); } } int main() { vector<int> v1{ 0, 0, 30, 20, 0, 0, 0, 0, 10, 0 }; vector<int> v2; const int unexpectedInt = 0; /* 方案一: 利用數組下標sanzho */ FiltArray0(v1, v2, unexpectedInt); cout << "利用數組下標方案,V2中數據爲:" << endl; ShowArray(v2); /* 方案二: 利用迭代器 */ ClearArray(v2); FiltArray1(v1, v2, unexpectedInt); cout << "利用迭代器方案,V2中數據爲:" << endl; ShowArray(v2); /* 方案三: 利用拷貝算法 */ ClearArray(v2); FiltArray2(v1, v2, unexpectedInt); cout << "利用拷貝算法,V2中數據爲:" << endl; ShowArray(v2); return 0; }
運行了幾回,來觀察實際運行時間。
等等。綜合發現DEBUG模式下。
第三種方案的運行時間最長,代碼量最少。
第二種方案的運行時間最長,更爲通用。
第一種方案的運行時間居中,但不通用。
Release模式下,數據以下圖所示:
數據整理,對好比下表所示:
下標操做 | 迭代器操做 | remove_copy_if()算法操做 | |
Debug統計數據一 | 0.000015762 | 0.0000395882 | 0.00000733115 |
Debug統計數據二 | 0.00000879738 | 0.000023093 | 0.00000806426 |
Debug統計數據三 | 0.00000879738 | 0.0000373889 | 0.00000733115 |
Release模式一 | 0.00000146623 | 0 | 0 |
Release模式二 | 0.00000146623 | 0.000000366557 | 0.000000366557 |
Release模式三 | 0.00000146623 | 0.00000109967 | 0.000000366557 |
對比發現,Release版本通過優化後,模式使用迭代器耗費的時間下降了很多。此時居然比下標運行時間還要短!
1)效率相比本身手寫更高;STL的代碼都是C++專家寫出來的,專家寫出來的代碼在效率上很難超越;
2)千萬注意要使用++iter 不能使用iter++,iter++ 是先拷貝一份值,再進行++,效率很低;
3)經過分析,用algorithm+functional進行遍歷效率最高。並且 下標索引的方式老是會效率高於迭代器方式。
iterator end() _NOEXCEPT { // return iterator for end of mutable sequence return (iterator(this->_Mylast, this)); } const_iterator end() const _NOEXCEPT { // return iterator for end of nonmutable sequence return (const_iterator(this->_Mylast, this)); }
vector是STL中的一種序列式容器,採用的數據結構爲線性連續空間,它以兩個迭代器 start 和 finish 分別指向配置得來的連續空間中目前已被使用的範圍,並以迭代器end_of_storage 指向整塊連續空間(含備用空間)的尾端,結構以下所示:
template Alloc = alloc>
class vector {
...
protected:
iterator start; // 表示目前使用空間的頭
iterator finish; // 表示目前使用空間的尾
iterator end_of_storage; // 表示可用空間的尾
...};
咱們在使用 vector 時,最常使用的操做恐怕就是插入操做了(push_back),那麼當執行該操做時,該函數都作了哪些工做呢?
該函數首先檢查是否還有備用空間,若是有就直接在備用空間上構造元素,並調整迭代器 finish,使 vector 變大。若是沒有備用空間了,就擴充空間,從新配置、移動數據,釋放原空間。
其中判斷是否有備用空間,就是判斷 finish 是否與 end_of_storage 相等.若是
finish != end_of_storage,說明還有備用空間,不然已無備用空間。
當執行 push_back 操做,該 vector 須要分配更多空間時,它的容量(capacity)會增大到原來的 m 倍。如今咱們來均攤分析方法來計算 push_back 操做的時間複雜度。
假定有 n 個元素,倍增因子爲 m。那麼完成這 n 個元素往一個 vector 中的push_back操做,須要從新分配內存的次數大約爲 logm(n),第 i 次從新分配將會致使複製 m^i (也就是當前的vector.size() 大小)箇舊空間中元素,所以 n 次 push_back 操做所花費的總時間約爲 n*m/(m - 1):
很明顯這是一個等比數列.那麼 n 個元素,n 次操做,每一次操做須要花費時間爲 m / (m - 1),這是一個常量.
因此,咱們採用均攤分析的方法可知,vector 中 push_back 操做的時間複雜度爲常量時間.