STL<vector>用法彙總

使用場合:

vector算是一個比較萬金油的容器,它是一個可變大小數組,支持隨機訪問,不過在尾部以外的位置進行增加和刪除操作會比較耗時。通常用vector來代替原始的數組來使用,比較方便。

聲明與初始化:

首先要包含頭文件,vector的頭文件名就是< vector >。

聲明方式:

#include <bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    vector<int> ve;//聲明一個存儲int型數據的容器ve
    vector<vector<int>> vve;//聲明一個二維的vector,C++11標準寫法
    vector<vector<string> > vvs;//兩個尖角括號之間要留一個空格,舊編譯器的寫法
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

初始化:

vector的初始化方式很多,書上介紹的如下

#include <bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    vector<int> va;//調用默認構造函數,裏面什麼也沒有
    for(int i=0;i<5;i++)
        va.push_back(i);//放5個元素進去
    vector<int> vb(va);//用va初始化vb,此種用法要求va與vb必須是同一種容器,且類型相同
    vector<int> vc{1,2,3,4};//初始化列表
    vector<int> vc2={1,2,3,4};//同上,C++11新標準
    vector<int> vd(va.begin(),va.end());//用迭代器指定的範圍初始化

    list<int> li={2,3,4};
    vector<int> vli(li.begin(),li.end())//使用迭代器可以把不同容器,類型相同的元素用來初始化

    vector<int> ve(10);//包含10初始化值的元素,在ve當中裏面有10個0次構造函數是explicit
    vector<int>  vf(10,1);//在vf裏面塞進10個1
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

賦值操作:

利用拷貝構造函數和swap函數可以實現賦值操作

#include <bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    vector<int> va,vb,vc;

    va={1,2,3,4};//用C++11的初始化列表來賦值
    for(int i=1;i<=10;i++)//在vb中塞入10個數
        vb.push_back(i);

    va=vb;//把vb拷貝給va

    swap(va,vc);//交換va和vc
    for(auto x:vc)//輸出應該是va裏面的值
        cout<<x<<" ";
    for(auto x:va)//va裏面應該什麼也沒有
        cout<<x<<" ";

    cout<<endl;

    va.swap(vc);//再把vc和va換回來,用成員函數的形式
    for(auto x:vc)//裏面什麼也沒有
        cout<<x<<" ";
    for(auto x:va)//
        cout<<x<<" ";
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

還可以利用assign函數實現

#include <bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    vector<int> vb,va={1,3,5,7,9};
    vb.assign(va.begin(),va.begin()+3);//使用va的迭代器給vb賦值
    //這裏注意,不可以自己給自己用迭代器賦值
    for(int i=0;i<vb.size();i++)
        cout<<vb[i]<<endl;//輸出1 3 5

    vb.assign({2,4,6});//裏面可以放一個初始化列表
    for(int i=0;i<vb.size();i++)
        cout<<vb[i]<<endl;//輸出2 4 6

    list<int> li={2,4,6,8};
    vb.assign(li.begin(),li.end());//可以把其他容器,但是類型相同的迭代器用來賦值
    for(int i=0;i<vb.size();i++)
        cout<<vb[i]<<endl;

    list<string> names={"abc","efg"};
    vector<const char*> oldstyle={"qwe","ert"};
    names.assign(oldstyle.begin(),oldstyle.end());//vector給list初始化,也可以用cbegin
    //把const cahr*賦值給string
    for(auto x:names)
        cout<<x<<endl;//qwe ert

    vector<string> vc;
    vc.assign(3,"abc");//向vc中塞入3個abc
    for(int i=0;i<vc.size();i++)
        cout<<vc[i]<<endl;//abc abc abc
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

vector的比較

vector重載了運算符<,>,==,<=,>=等比較符號。其比較規則與字典序類似,如果兩個容器具有相同大小,而且每個元素對應相等,那麼這兩個vector相同。 
如果兩個vector大小不同,但是公有的元素和對應位置全都相同,那麼大的容器比小的容器大。 
如果兩個容器完全不同,那麼,比較第一個不同的元素哪個大那個小作爲判斷標準。

#include <bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    vector<int> v1={1,3,5};
    vector<int> v2={1,4};
    vector<int> v3={1,3,5};
    cout<<(v1<v2)<<endl;//true
    cout<<(v1==v3)<<endl;//true
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

插入元素:

vector當中插入元素主要使用push_back向後面插入一個元素。 
在vector當中沒有push_front的用法! 
如果想再任意位置插入一個元素,可以用insert成員函數,同樣會涉及到數據移動。 
具體用法:

#include <bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    vector<int> ve;

    //ve.push_back(e)在ve的尾部添加一個e元素
    ve.push_back(1);//在尾部插入一個1

    //ve.insert(it,n,e)//在it指向元素的前面添加n個元素e
    ve.insert(ve.end(),1,2);//相當於在尾部添加1個2
    ve.insert(ve.begin(),1,0);//在頭部添加1個0

    //ve.insert(it,beg,ed)//在it指向元素的前面添加迭代器[beg,ed)範圍內的元素
    vector<int> v={-2,-1};
    ve.insert(ve.begin(),v.begin(),v.end());//在ve的前面添加v的元素

    //ve.insert(it,li)在it所指的元素的前面添加一個列表li
    ve.insert(ve.end(),{3,4,5});

    //錯誤用法
    //ve.insert(ve.begin(),ve.begin(),ve.end())不能用調用對象自己的迭代器當做賦值範圍
    for(auto x:ve)
        cout<<x<<endl;
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

這裏的push_back操作是對拷貝值進行操作。 
在C++11標準當中,insert函數具有返回值,返回第一個新加入元素的迭代器(不能是插入列表)。

#include <bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    vector<int> ve={1,2,3,4};

    auto it=ve.insert(ve.end(),5);//返回指向最後一個元素的迭代器
    cout<<*it<<endl;//5

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

emplace操作:

當調用emplace操作的函數時,每次會新構造一個對象,並添加到容器當中。添加的方法要和類當中的構造函數相匹配。 
在vector當中有c.emplace()和c.emplace_back()操作兩種。參數當中可以

#include <bits/stdc++.h>
using namespace std;
class A
{
public:
    int a;
    string s;
    A(){a=0,s="";}
    A(int aa,string ss){a=aa,s=ss;}
};
int main()
{
    ios::sync_with_stdio(false);
    vector<A> va;
    va.emplace_back(1,"abc");//添加
    va.emplace_back();//添加一個默認構造函數
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

也可以調用c.emplace(it,arg);其中it爲指向一個數據的迭代器,arg爲參數。

訪問元素:

  • 清空vector可以使用成員函數c.clear()
  • 判斷vector是否爲空,可以使用成員函數empty(),如果爲空返回true,否則返回false
  • vector輸出最後一個元素的引用可以用back()成員函數,如果容器爲空,則行爲未定義
  • vector輸出第一個元素的引用可以用front()成員函數,如果容器爲空,則行爲未定義
  • vector支持用下標訪問元素,類似數組一樣c[n]其中n是一個無符號整數,如果n大於容器的長度,那麼行爲未定義
  • vector爲了防止越界訪問,其中有成員函數c.at(n),返回下標爲n的元素的引用。如果下標越界,那麼拋出out_of_range的異常

以上返回引用會根據容器的類型來判斷,也就是說如果聲明瞭一個const容器,那麼返回一個const的引用,否則返回一個普通的引用

#include <bits/stdc++.h>
using namespace std;

int main()
{
    ios::sync_with_stdio(false);
    vector<int> ve={1,2,3,4};
    ve.back()=5;//4變成5
    ve.front()=0;//1變成0
    cout<<ve[0]<<" "<<ve[ve.size()-1]<<endl;
    try
    {
        ve.at(6);
    }
    catch(out_of_range &e)
    {
        cerr<<e.what()<<endl;
    }
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

刪除元素

vector當中刪除元素通常使用pop_back()用來刪除最後一個元素,也可以使用erase()成員函數來刪除任意位置的元素。由於vector的性質,刪除任意位置的元素會降低效率。

  • pop_back()成員函數用來刪除vector中的最後一個元素,如果容器爲空會出現未定義行爲。
  • c.erase(it)成員函數,刪除迭代器it所指向的元素,返回一個指向被刪除元素之後的迭代器,如果it指向最後一個元素,那麼返回以爲尾後迭代器(通常是end())。若it就是end(),那麼行爲未定義。
  • c.erase(beg,ed)刪除[beg,ed)範圍的元素,同時返回最後一個元素的後面的迭代器,如果ed就是尾後迭代器,那麼還返回一個尾後迭代器。
#include <bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    vector<int> ve={1,2,3,4};
    ve.erase(ve.begin(),ve.begin()+1);//刪除第一個元素,結果是2 3 4

    ve.erase(ve.begin());//刪除第一個元素,結果是3 4

    ve.pop_back();//刪除最後一個元素,結果變成3
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

注意,刪除元素以後會使迭代器失效,所以循環刪除元素這樣寫是錯誤的。

#include <bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    vector<int> ve={1,2,3,4,5,6,7,8,9};
    for(auto it=ve.begin();it!=ve.end();it++)//錯誤寫法
        if((*it)%2)//刪除奇數的元素
            ve.erase(it);

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

正確的刪除方式

#include <bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    vector<int> ve={1,2,3,4,5,6,7,8,9};
    for(auto it=ve.begin();it!=ve.end();)//沒毛病
    {
        if((*it)%2)
            ve.erase(it);
        else
            it++;
    }
    for(auto x:ve)
        cout<<x<<endl;
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

謹記,改變容器的長度的操作會使迭代器實效。

vector的容量與內存管理:

vector是一個可以增長長度的容器,用戶可以多自行改變容器長度。C++11標準當中爲了節約內存,新增加了一個函數用來回收多餘的內存。

  • size()成員函數,返回容器當中元素的個數。
  • resize(n)成員函數,重新定義容器的元素的個數。注意,不是容量。如果n大於當前的size(),那麼多出的長度用默認值補充,如果n小於當前的size()那麼只留下前n個元素,剩下的刪除。
  • resize(n,t)成員函數,原則同上,只不過多出的元素用t補充而已
#include <bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    vector<int> ve={1,2,3,4,5,6,7,8,9};
    cout<<ve.size()<<endl;//9
    ve.resize(15);
    cout<<ve.size()<<endl;//15 多出來的用默認值補充

    ve.resize(3);//就留下前3個數,剩下的都刪除

    ve.resize(10,-1)//多出的元素用-1補充
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • capacity()成員函數是不重新分配內存的情況下,容器可以保存多少元素
  • reserve(n)成員函數是分配智商能容納n個元素的內存空間。比如ve當中有三個元素,現在使用ve.reserve(1),那麼沒有變化。如果使用ve.reserve(10),那麼ve.size()還是原來的值,不過ve.capacity()會變成10
#include <bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    vector<int> ve;
    for(int i=0;i<100;i++)//放入100個元素
        ve.push_back(i);
    cout<<ve.size()<<endl;//100
    cout<<ve.capacity()<<endl;//128

    ve.reserve(99);//比100小,所以什麼也不做

    cout<<ve.size()<<endl;//100
    cout<<ve.capacity()<<endl;//128

    ve.reserve(150);

    cout<<ve.size()<<endl;//100
    cout<<ve.capacity()<<endl;//150
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

最後說一下C++11的新函數,shrink_to_fit()說簡單點,就是把多餘沒用到的空間回收一下,這個函數是向系統提交一個申請,而不是一個強制命令。

#include <bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    vector<int> ve;
    for(int i=0;i<100;i++)//放入100個元素
        ve.push_back(i);
    cout<<ve.size()<<endl;//100
    cout<<ve.capacity()<<endl;//128

    ve.shrink_to_fit();

    cout<<ve.size()<<endl;//100
    cout<<ve.capacity()<<endl;//100

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

可能會覺得,那每次使用vector後都調用這個函數,不就很省內存了嘛。此言差矣,如果需要再向容器當中塞東西,那麼就要重新給容器增加分配的內存,更浪費時間和效率。

多維vector的用法:

用二維向量做例子,所謂多維向量,就是一個向量一面存儲的內容是一個向量。通常可以代替二維數組來使用。也可以使用向量的數組來表示。

二維向量的聲明:

vector<vector<int>> vve;//在新版本的編譯器當中這樣寫沒問題
vector<vector<int> >vvve;//舊版本的編譯器會把兩個相連的尖括號當成流操作
  • 1
  • 2

初始化:

#include <bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    vector<vector<int>> vva(10,vector<int>(0));//vve裏面放10個vector,每個vector裏面初始化一個0元素
    vector<vector<int>> vvb(10,{1,2,3,4});//v裏面放10個vector,每個vector用列表初始化
    vector<int> va={0,2,3};
    vector<vector<int>> vvc(3,va);//vvc中裏面放入3個va

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

總之,和初始化一個普通的vector沒什麼兩樣,只不過就是初始化的元素變成了vector而已

插入:

這裏使用push_back和insert

#include <bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    vector<vector<int>> vvb(4,{1,2,3,4});
    for(int i=0;i<3;i++)
    {
        vector<int> vtemp={i};
        vvb.push_back(vtemp);//每次塞入一個用i初始化過的vtemp
    }
    vector<int> vt={666};
    vvb.insert(vvb.begin(),vt);//把vt放在vvb的頭部
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

遍歷:

一般使用下標遍歷、迭代器遍歷

#include <bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    vector<vector<int>> vvb(4,{1,2,3,4});
    for(int i=0;i!=vvb.size();i++)
    {
        for(int j=0;j!=vvb[i].size();j++)
            cout<<vvb[i][j]<<" ";
        cout<<endl;
    }
    for(auto bit=vvb.begin();bit!=vvb.end();bit++)
    {
        for(auto bbit=bit->begin();bbit!=bit->end();bbit++)//bit類似指向一個vvb[i],裏面是向量中的元素
            cout<<*bbit<<" ";
        cout<<endl;
    }
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

當然,也可以用C++11的新遍歷方式

#include <bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    vector<vector<int>> vvb(4,{1,2,3,4});
    for(auto x:vvb)
    {
        for(auto xx:x)
            cout<<xx<<" ";
        cout<<endl;
    }
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

刪除操作:

和一維vector刪除元素沒什麼區別

#include <bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    vector<vector<int>> vvb(4,{1,2,3,4});
    vvb.pop_back();//刪除最後一個元素
    vvb.erase(vvb.begin());//刪除第一個向量元素
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

三維,或者更高維的也是如此。

容器的指針,vector::data:

調用vector::data會返回一個指向第一個元素的指針,由於vector的內存是連續的,所以指針的移動,也就對應vector當中的元素遍歷。看代碼

#include <bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    std::vector<int> myvector (5);

    int* p = myvector.data();
    *p = 10;
    ++p;
    *p = 20;
    p[2] = 100;
    cout << "myvector contains:";
    for(unsigned i=0; i<myvector.size(); ++i)
    cout << ' ' << myvector[i];
    cout << '\n';

  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

結果是myvector contains: 10 20 0 100 0 
上面的代碼來自http://www.cplusplus.com/reference/vector/vector/data/

小結:

vector的成員函數當中還有一個get_allocate,是獲取整塊內存類似,c語言當中memcpy函數。這裏先不介紹,後面補上。 
vector在C++代碼當中使用十分廣泛,可以用來表示矩陣,表示向量,當做鄰接表來使用,十分方便。 
加上與泛型算法和模板的搭配,使用功能也非常強大。 
現在就總結這些,在編程和學習當中如果遇到有關vector的問題,還會在這裏繼續補充。如果寫出來的觀點和代碼有什麼問題,請各位看官不吝指正,我會及時更改。

to be continue~

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