C++標準庫容器篇--順序容器

一、概述

1、標準庫定義了三種順序容器類型:vector、list 和 deque。
2、他們差別在於訪問元素 的方式,以及添加或刪除元素相關操作的運行代價。
3、單純的類容器只定義了少量操作,大多數額外操作由算法庫提供。
這裏寫圖片描述
這裏寫圖片描述

這裏寫圖片描述

二、容器的初始化

1、構造函數初始化

#include<iostream>
#include<vector>
#include<list>
#include<deque>
using namespace std;
int main()
{   
    vector<int> ivec;
    vector<int> ivec2(ivec);   // ok
    //list<int>   ilist(ivec);   // error
    //vector<double> dvec(ivec); // error
    vector<int> a(10);// a包含10個0值元素
    vector<int> b(10,1);//b爲10個值爲1的元素
}

2、用迭代器範圍賦值

int main()
{  
    vector<string> svec;
    //用下面方法不要求迭代器的元素類型或者容器類型完全相同,只要相互兼容即可,兼容性待考證
    list<string> slist(svec.begin(), svec.end());  //迭代器標記了複製元素的範圍,由此方法可以將其他容器的子序列複製
    vector<string>::iterator i = svec.begin() + svec.size() / 2;
    deque<string> front(svec.begin(), i);//前半部分給front
    deque<string> back(i, svec.end());
}

當然迭代器就是指針了,所以我們也可以用自定義的迭代器進行賦值:

#include<iostream>
#include<vector>
#include<list>
#include<deque>
#include<string>
using namespace std;
int main()
{
    char *kk[] = { "kkk", "zlk", "123", "abv", "love" };
    int size_kk = sizeof(kk) / sizeof(char*);
    list<string> list_kk(kk, kk + size_kk);
    vector<char*> vector_kk(kk, kk + size_kk);
    list<string>::iterator i = list_kk.begin();
    while (i != list_kk.end())
    {
        cout << *i++ << endl;
    }
    for (vector<char*>::iterator j = vector_kk.begin(); j < vector_kk.end(); j++)
    {
        cout << *j << endl;
    }
}

指定數目初始化:

const list<int>::size_type list_size = 64;
list<sting> kk(list_size,"kk");//kk是含有64個元素,每個都是kk的容器
list<string> kk(list_size);//64個元素初始化爲0

問題:複製容器對象的構造函數和使用兩個迭代器的構造函數有什麼區別?
複製容器對象:只能將一個容器初始化爲另一個容器的副本,要求兩個容器的類型,元素的類型完全相等。
使用兩個迭代器:用一個容器的子序列初始化另外一個容器,不要求兩個容器的類型是相同的。

二、順序容器的迭代器

1、end()迭代器指向的從來就不是最後一個元素,而是最後一個元素的下一個位置!所以其迭代器範圍爲:
[begin(), end()),左開右閉區間。

2、順序容器中定義的類型別名
這裏寫圖片描述

三、對順序容器的操作

1、添加操作
這裏寫圖片描述
需要注意的是,vector和deque在添加元素時候會導致全部迭代器失效例如:

int main()
{   
    string k[] = { "i", "have", "a", "dream" };
    deque<string>kk = {k,k+4};
    string word("ha");
    deque<string>::iterator first = kk.begin(), last = kk.end();
    while (first!=last)
    {
        kk.insert(first, word);
        first++;
    }
}

在用了循環體的添加運算後,last 失效,不再指向kk.end();
所以:
不要存儲 end 操作返回的迭代器。添加或刪除 deque 或 vector 容器內的元素都會導致存儲的迭代器失效。
以下是正確的代碼:

int main()
{   
    string k[] = { "i", "have", "a", "dream" };
    deque<string>kk = {k,k+4};
    string word("ha");
    deque<string>::iterator first = kk.begin(), last = kk.end();
    while (first!=kk.end())
    {
        first=kk.insert(first, word);
        ++first;
        ++first;
    }
    for (deque<string>::iterator i = kk.begin(); i != kk.end(); i++)
    {
        cout << *i << endl;
    }
}

這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
注意:vector容器無法進行pop_front從前刪除操作
這裏寫圖片描述

補充:

一、vector的size和capacity的區別:

int main()
{   
    vector<int> kk;
    cout << kk.size() << endl;//0
    cout << kk.capacity() << endl;//0
    for (int i = 0; i < 20; i++)
    {
        kk.push_back(i);
    }
    cout << kk.size() << endl;//20
    cout << kk.capacity() << endl;//28
    kk.reserve(50);
    cout << kk.size() << endl;//20
    cout << kk.capacity() << endl;//50  
}

二、順序容器的選擇法則:

a. 如果程序要求隨機訪問元素,則應使用 vector 或 deque 容器。
b. 如果程序必須在容器的中間位置插入或刪除元素,則應採用 list 容器。
c. 如果程序不是在容器的中間位置,而是在容器首部或尾部插入或刪除元 素,則應採用 deque 容器。
d. 如果只需在讀取輸入時在容器的中間位置插入

原因:
1、vector 容器,除了容器尾部外,其他任何位置上的插入(或刪除)操 作都要求移動被插入(或刪除)元素右邊所有的元素。

2、list 容器不支持隨機訪問,訪問某 個元素要求遍歷涉及的其他元素。但list容器可以高效地 insert 或 erase 一個元素。

3、在 deque 容器首部或尾部插入元素不會使任何迭代器失效,而首部或尾 部刪除元素則只會使指向被刪除元素的迭代器失效。

三、支持順序容器操作的string
理解:string的很多功能也是用stl實現的,所以支持很多順序容器的操作
這裏寫圖片描述
用迭代器初始化string:

char *cp = "Hiya";            // null-terminated array      
char c_array[] = "World!!!!"; // null-terminated      
char no_null[] = {'H', 'i'};  // not null-terminated 

string s1(cp);             // s1 == "Hiya"      
string s2(c_array, 5);     // s2 == "World"      
string s3(c_array + 5, 4); // s3 == "!!!!"      
string s4(no_null);        // runtime error: no_null not null-terminated      
string s5(no_null, 2);     // ok: s5 == "Hi" 

特殊的一些函數:

string s("hello world");      // return substring of 5 characters starting at position 6      string s2 = s.substr(6, 5);   // s2 = world 
string s3=s.append("!!!!");   // s3= hello world!!!!
int i=s.find("world");        //i=6 是world第一次出現的下標,沒有找到會返回-1
string kong(" ");
int j = s.find_first_of(kong);//j爲5,查找第一次出現kong內字符的位置

例題1:搜索字符串內的數字和字母:

int main()
{   
    int x = 0, y = 0, z = 0;
    string k = ("ab2c3d7R4E6");
    string num = ("0123456789");
    string ar = ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
    int a1[100];
    int a2[100];
    while (1)
    {
        z = k.find_first_of(num, z); //find_first_of函數要求z<k.size()-1,即z小於最後一個元素的下標!!
        cout << "下標爲:" << z << "值爲:" << k[z] << endl;

        if ((z >= k.size()-2)||z==-1)
        {
            break;
        }
        ++z;
    }
    z = 0;
    cout << endl;
    while (1)
    {
        z = k.find_first_of(ar, z); //z<k.size()-1
        cout << "下標爲:" << z << "值爲:" << k[z] << endl;

        if ((z >= k.size() - 2) || z == -1)
        {
            break;
        }
        ++z;
    }
    cout << endl;
    cout << k.size();
}

例題2:查找單詞個數,與最長最短單詞

//單詞分隔符號爲空格的時候
int main()
{   
    string line1 = "We were her pride of 10 she named us:";      
    string line2 = "Benjamin, Phoenix, the Prodigal";
    string line3 = "and perspicacious pacific Suzanne";
    string sentence = line1 + ' ' + line2 + ' ' + line3;
    string temps;
    int counter = 0;
    int max = 0;
    int min = 9999;
    stringstream ss(sentence);
    while (ss >> temps)
    {
        ++counter;
        if (temps.size() > max)
        {
            max = temps.size();
        }
        if (temps.size() < min)
        {
            min = temps.size();
        }
    }
    cout << "單詞數爲:" << counter << endl;
    stringstream sss(sentence);
    while (sss >> temps)
    {
        if (temps.size() == max)
        {
            cout << "最長單詞爲:" << temps << endl;
        }
        if (temps.size() == min)
        {
            cout << "最短單詞爲:" << temps << endl;
        }
    }
}
//單詞分隔符號任意(可爲,:\t\n的時候)
int main()
{
    string line1 = "We were her pride of 10 she named us:";
    string line2 = "Benjamin, Phoenix, the Prodigal";
    string line3 = "and perspicacious pacific Suzanne";
    string sentence = line1 + ' ' + line2 + ' ' + line3;
    string temps;
    vector<string> maxs;
    vector<string> mins;
    int max = 0, min = 9999;
    int counter = 0;
    int wordlen;
    int start = 0, end = 0;
    string fenge(" ,.\t\n\\");
    while (1)
    {
        start = sentence.find_first_not_of(fenge, end); //start是從結束座標開始第一個不是分隔符的元素下標
        ++counter;
        end = sentence.find_first_of(fenge, start);//end變爲單詞末尾的下一個分隔符下標
        if (end == -1)
        {
            break;
        }
        wordlen = end - start;
        if (wordlen > max)
        {
            maxs.clear();
            temps.assign(sentence.begin() + start, sentence.begin() + end);
            maxs.push_back(temps);
            max = wordlen;
        }
        else if (wordlen == max)
        {
            temps.assign(sentence.begin() + start, sentence.begin() + end);
            maxs.push_back(temps);
        }
        if (wordlen < min)
        {
            mins.clear();
            temps.assign(sentence.begin() + start, sentence.begin() + end);
            mins.push_back(temps);
            min = wordlen;
        }
        else if (wordlen == min)
        {
            temps.assign(sentence.begin() + start, sentence.begin() + end);
            mins.push_back(temps);
        }
        if (start > sentence.size() - 2 || end > sentence.size() - 2)
        {
            break;
        }
    }
    cout <<"單詞數目爲:" <<counter << endl;
    for (vector<string>::iterator iter = maxs.begin(); iter != maxs.end(); iter++)
    {
        cout << "最長的單詞爲" << *iter << endl;
    }
    for (vector<string>::iterator iter = mins.begin(); iter != mins.end(); iter++)
    {
        cout << "最短的單詞爲" << *iter << endl;
    }
}

容器適配器

除了順序容器,標準庫還提供了三種順序容器適配器:queue、priority_queue和stack
一、什麼是容器適配器:
容器適配器讓一種已存在的容器類型採用另一種 不同的抽象類型的工作方式實現。例如,stack(棧)適配器可使任何一種順序容器以棧的方式工作。

deque<int> deqint;
stack<int>  stk(deqint);// 用deqint容器初始化一個新的棧
stack<string, vector<string>> string_stk; //在vector的基礎上覆蓋空棧

實現:
1、默認的 stack 和 queue 都基於 deque 容器實現
2、默認的priority_queue 則在 vector 容器上實現。
有以下規則:
1、stack 棧可以建立在 vector、list 或者 deque 容器之上。
2、queue 適配器要求其關聯的基礎容器 必須提供 push_front 運算,因此只能建立在 list 容器上(此處我認爲deque也可以,書上的表述應該不清楚),而不能建立在 vector 容器上。
3、priority_queue 適配器要求提供隨機訪問功能,因此可建立在 vector 或 deque 容器上,但不能建立在 list 容器。

二、棧適配器stack
先進後出

int main()
{
    const int size = 10;
    stack<int> sta_int;
    int i = 0, j = 1;
    while (sta_int.size() != size)
    {
        int tem = j;
        sta_int.push(j);  //元素壓棧
        j += i;
        i = tem;
    }
    while (!sta_int.empty())
    {
        int val = sta_int.top(); //讀出棧頂元素
        sta_int.pop();           //刪除棧頂元素,完成棧頂出棧過程
        cout << val << ends;
    }
}

三、適配器隊列和優先級隊列
queue標準隊列:先進先出,新進的元素在隊尾;
priority_queue優先級隊列:用戶爲隊列設置優先級,新進入的元素比較優先級,將自己放在比他低優先級的元素前面;
這裏寫圖片描述

四、關於這幾個適配器的應用實例
1、stack應用:括號匹配

//遇到左括號的時候將其標記,遇到右括號時彈出括號間的元素
int main()
{
    stack<char> sta_char;
    string exp;
    cin >> exp;
    string::iterator iter = exp.begin();
    while (iter != exp.end())
    {   
        if (*iter == '(')
        {
            ++iter;
            while (*iter != ')')
            {
                sta_char.push(*iter);
                ++iter;
            }
            while (!sta_char.empty())
            {
                cout << sta_char.top()<<ends;
                sta_char.pop();
            }
            sta_char.push('#');//表示表達式已經彈出
        }
        ++iter;
    }
}

2、queue的應用:圖的廣度優先遍歷

//無向圖的廣度優先遍歷
#include<iostream>
#include<queue>
using namespace std;
#define MAX 20
int map[MAX][MAX];
int arr[MAX] = { 0 }; //標記是否已經被訪問
void bfs(int n)                         //圖的廣度優先遍歷
{
    int top;
    queue<int> que;
    arr[0] = 1;
    cout << 1 << ends;
    for (int i = 0; i < n; i++)
    {
        if (map[0][i] == 1 && arr[i] == 0)  //從第0個頂點開始入隊聯通且沒有被標記的點
        {
            que.push(i);
            arr[i] = 1;
        }
    }
        while (!que.empty())
        {
            top = que.front();
            que.pop();
            cout << top+1 << ends;
            for (int i = 0; i < n; i++)
            {
                if (map[top][i] == 1 && arr[i] == 0)
                {
                    que.push(i);
                    arr[i] = 1;
                }
            }
        }
}
int main()
{
    int vex, edge, x, y;
    cin >> vex >> edge;
    for (int i = 0; i < MAX; i++)
    {
        for (int j = 0; j < MAX; j++)
            map[i][j] = 0;
    }
    for (int i = 0; i < edge; i++)
    {
        cin >> x >> y;
        map[x-1][y-1] = map[y-1][x-1] = 1;
    }
    bfs(vex);
}

3、最高獎勵問題:

//有N個任務,每個任務有一個最晚結束時間以及一個對應的獎勵。
//在結束時間之前完成該任務,就可以獲得對應的獎勵。完成每一個任務所需的時間都是1個單位時間。
//有時候完成所有任務是不可能的,因爲時間上可能會有衝突,這需要你來取捨。
//求能夠獲得的最高獎勵。

//input:第1行:一個數N,表示任務的數量(2 <= N <= 50000)
//第2 - N + 1行,每行2個數,中間用空格分隔,表示任務的最晚結束時間E[i]以及對應的獎勵W[i]。
//(1 <= E[i] <= 10 ^ 9,1 <= W[i] <= 10 ^ 9)
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
struct r{
    int e, w;
    friend bool operator< (r a, r b)   //定義了比較方式之後,優先級隊列的算符也會改變 
    {
        return a.w>b.w;              //將價值最低的放在top用以替換
    }
};
//分析一波:每個任務完成需要一個單位時間。那麼不妨先將任務按結束時間排序。
//依次遍歷每個任務,如果當前時間足夠完成該任務,則加入隊列,如果衝突,則比較隊列中價值最小的元素,
//如果該任務價值高,則替換之。
int cmp(r a, r b)  //sort 函數的從小到大排序
{
    if (a.e < b.e)
    {
        return true;
    }
    else
        return false;
}

int main()
{
    r p[20];
    int N;
    cin >> N;
    for (int i = 0; i < N; i++)
    {
        cin >> p[i].e >> p[i].w;
    }
    //建立一個優先級隊列,將結束時間晚的放到最後,爭取利益最大化
    priority_queue<r> pro_que;
    sort(p, p + N, cmp);  //先把所有任務按時間排序(從小到大)
    for (int i = 0; i < N; i++)
    {
        if (pro_que.size() < p[i].e) //如果當前隊列執行完的時間小於 任務的結束時間(任務未結束)
        {
            pro_que.push(p[i]); //按照任務價值排進優先級隊列
        }
        else if (p[i].w>pro_que.top().w)  //如果當前時間等於任務結束的時間(即可以替換)
        {
            pro_que.pop();
            pro_que.push(p[i]);
        }
    }
    long ans = 0;
    while (!pro_que.empty())
    {
        ans += pro_que.top().w;
        pro_que.pop();
    }
    cout << ans;

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