STL

1.容器

STL容器包含順序式容器和關聯式容器兩類。

順序式容器 特點
vector 動態數組,從末尾了以快速插入與刪除,直接訪問任何元素
list 雙鏈表,從任何地方快速插入與刪除
deque 雙向隊列,從前面或後面快速插入與刪除,直接訪問任何元素
queue 隊列,先進先出
priority_queue 優先隊列,最高優先級元素總是第一個出列
stack 棧,先進後出
關聯式容器 特點
set 快速查找,不允許重複值
multiset 快速查找,允許重複值
map 一對多映射,基於關鍵字快速查找,不允許重複值
multimap 一對多映射,基於關鍵字快速查找,允許重複值
  • stack
    棧是一個先進先出的數據結構,使用STL中的stack時需要包含頭文件 #include<stack> ,常用操作如下:

    操作 操作結果
    top() 返回一個棧頂元素的引用,類型爲 T&。如果棧爲空,返回值未定義
    push(const T& obj) 可以將對象副本壓入棧頂。這是通過調用底層容器的 push_back() 函數完成的。
    push(T&& obj) 以移動對象的方式將對象壓入棧頂。這是通過調用底層容器的有右值引用參數的 push_back() 函數完成的。
    pop() 彈出棧頂元素。
    size() 返回棧中元素的個數。
    empty() 在棧中沒有元素的情況下返回 true。
    emplace() 用傳入的參數調用構造函數,在棧頂生成對象。
    swap(stack & other_stack) 將當前棧中的元素和參數中的元素交換。參數所包含元素的類型必須和當前棧的相同。對於 stack 對象有一個特例化的全局函數 swap() 可以使用。

    Text Reverse

    【題目】Text Reverse
    Ignatius likes to write words in reverse way. Given a single line of text which is written by Ignatius, you should reverse all the words and then output them.

    【Input】
    The input contains several test cases. The first line of the input is a single integer T which is the number of test cases. T test cases follow.
    Each test case contains a single line with several words. There will be at most 1000 characters in a line.
    【Output】
    For each test case, you should output the text which is processed.

    【Sample Input】
    3
    olleh !dlrow
    m’I morf .udh
    I ekil .mca
    【Sample Output】
    hello world!
    I’m from hdu.
    I like acm.

    【Hint】
    Remember to use getchar() to read ‘\n’ after the interger T, then you may use gets() to read a line and process it.

    代碼:

    //題目鏈接:https://vjudge.net/contest/337673#problem/B
    #include<bits/stdc++.h>
    using namespace std;
    int main()
    {
    int n;
    char ch;
    scanf("%d", &n);
    getchar();
    while(n--)
    {
        stack<char> s;
        while(true)
        {
            ch = getchar();//一次讀入一個字符
            if(ch==' ' || ch=='\n' || ch==EOF)
            {
                while(!s.empty())
                {
                    printf("%c", s.top());
                    s.pop();//彈出棧頂元素
                }
                if(ch=='\n' || ch==EOF) break;
                printf(" ");
            }
            else s.push(ch);//入棧
        }
        printf("\n");
    }
    return 0;
    }
    
    

    簡單計算器

    【題目】
    讀入一個只包含 +, -, *, / 的非負整數計算表達式,計算該表達式的值。

    【Input】
    測試輸入包含若干測試用例,每個測試用例佔一行,每行不超過200個字符,整數和運算符之間用一個空格分隔。沒有非法表達式。當一行中只有0時輸入結束,相應的結果不要輸出。
    【Output】
    對每個測試用例輸出1行,即該表達式的值,精確到小數點後2位。

    【Sample Input】
    1 + 2
    4 + 2 * 5 - 7 / 11
    0
    【Sample Output】
    3.00
    13.36

    代碼:

    //題目鏈接:https://vjudge.net/contest/337673#problem/C
    //STL - stack
    #include<bits/stdc++.h>
    using namespace std;
    
    double f(double k1, char c, double k2)
    {
        switch(c)
        {
        case '+':
            return k1+k2;
        case '-':
            return k1-k2;
        case '*':
            return k1*k2;
        case '/':
            return k1/k2;
        }
    }
    
    int main()
    {
        while(1)
        {
            int i=0, x;
            char a[210];//存放操作符
            int b[210];//存放操作數
            char c, d;
            cin>> x;
            if(x==0 && getchar() == '\n') break;//結束標誌
            do
            {
                cin>> d;
                getchar();
                b[i++] = x;
                a[i++] = d;
                cin>> x;
            }while(getchar()!='\n');
            b[i++] = x;
    
            stack<double> nu;//number stack
            stack<char> ch;//char stack
    
            double ans, k1, k2;//運算結果-前操作數-後操作數
    
            for(int j=0; j<i; j++)
            {
                if(j%2 == 0)//number
                {
                    int k=b[j];
                    nu.push((double)k);
                }
                else//character
                {
                    if(ch.empty())
                    {
                        if(a[j]=='*' || a[j]=='/')//乘除兩種運算符不放在棧底
                        {
                            char s=a[j];//保存運算符
                            k1=nu.top();
                            nu.pop();//讀取棧頂元素後將其出棧
                            k2=b[++j];//向後讀取後操作數
                            ans = f(k1, s, k2);
                            nu.push(ans);//運算結果入棧
                        }
                        else ch.push(a[j]);//棧空,直接將符號壓入棧
                    }
                    else
                    {
                        if((a[j] == '*' || a[j] == '/') /*&& (ch.top() == '+' || ch.top() == '-')*/)
                        {
                            char s=a[j];//保存運算符
                            //int ans, k1=nu.top(), k2=(int)(a[++j]-'0');//讀取棧頂元素, 向後讀取後操作數
                            k1=nu.top();
                            nu.pop();//讀取棧頂元素後將其出棧
                            k2=b[++j];//向後讀取後操作數
                            ans = f(k1, s, k2);
                            nu.push(ans);//運算結果入棧
                        }
                        if((a[j] == '+' || a[j] == '-') && (ch.top() == '+' || ch.top() == '-'))
                        {
                            //int ans, k1, k2;
                            char c;
                            k2 = nu.top();
                            nu.pop();//後操作數出棧
                            k1 = nu.top();
                            nu.pop();//前操作數出棧
                            c = ch.top();
                            ch.pop();//操作符出棧
                            ans = f(k1, c, k2);
                            nu.push(ans);//運算結果入棧
                            ch.push(a[j]);//當前操作符入棧
                        }
                    }
                }
            }
            if(!ch.empty())
            {
                k2 = nu.top();
                nu.pop();//後操作數出棧
                k1 = nu.top();
                nu.pop();//前操作數出棧
                c = ch.top();
                ch.pop();//運算符出棧
                ans = f(k1, c, k2);
            }
            else ans = nu.top();
            printf("%.2f\n", ans);
        }
        return 0}
    //思路:字符棧實際上總是不多於一個字符,並且保證棧底符號總是優先級最低的+或-,
    //遇見較高優先級時直接做運算,將結果壓入數棧即可。
    
    
     注意問題:
     爆戰問題:棧需要用空間儲存,如果深度太大,或者存進棧的數組太大,那麼總數會超過爲棧分配的空間,這樣會爆棧,即溢出。
     解決辦法:1.在程序中調大系統的棧,這種方法依賴於系統和編譯器。2.手工寫棧。
    
  • vector
    vector基礎知識參見:C++ vector 容器淺析
    值得注意的是,vector是以數組形式儲存的,也就是說他的內存空間是連續的,索引可以在常數時間內完成,但是在中間進行刪除和插入等操作時會造成內存塊的複製,這時要注意迭代器失效的問題。詳見迭代器失效的幾種情況總結

    圓桌問題

    【題目】
    圓桌上圍坐着2n個人。其中n個人是好人,另外n個人是壞人。如果從第一個人開始數數,數到第m個人,則立即處死該人;然後從被處死的人之後開始數數,再將數到的第m個人處死……依此方法不斷處死圍坐在圓桌上的人。試問預先應如何安排這些好人與壞人的座位,能使得在處死n個人之後,圓桌上圍坐的剩餘的n個人全是好人。

    【Input】
    多組數據,每組數據輸入:好人和壞人的人數n(<=32767)、步長m(<=32767);
    【Output】
    對於每一組數據,輸出2n個大寫字母,‘G’表示好人,‘B’表示壞人,50個字母爲一行,不允許出現空白字符。相鄰數據間留有一空行。

    【Sample Input】
    2 3
    2 4
    【Sample Output】
    GBBG

    BGGB

    代碼

        #include<bits/stdc++.h>
        using namespace std;
        int main()
     	{
         vector<int> table;
         int n, m;
         while(cin>> n>> m)
         {
             table.clear();
             for(int i=0; i<2*n; i++) table.push_back(i);//初始化
             int pos=0;//記錄當前位置
             for(int i=0; i<n; i++)//趕走n個人
             {
                 pos = (pos+m-1)%table.size();//圓桌是個環,進行取餘處理
                 table.erase(table.begin() + pos);//趕走壞人,人數減一
             }
             int j=0;
             for(int i=0; i<2*n; i++)
             {
                 if(!(i%50) && i) cout<< endl;//50個字母一行
                 if(j<table.size() && i==table[j])//table留下的都是好人
                 {
                     j++;
                     cout<< "G";
                 }
                 else
                     cout<< "B";
             }
             cout<< endl<< endl;
         }
         return 0;
     	}
    
    vector在插入或刪除中間某一項時需要線性時間,需要將後面的元素遷移或後移,複雜度是O(n),如果頻繁移動則效率很低。
    
  • deque
    deque(雙端隊列)是由一段一段的定量連續空間構成,可以向兩端發展,因此不論在尾部或頭部安插元素都十分迅速。 在中間部分安插元素則比較費時,因爲必須移動其它元素。

    容量函數
    size 返回容器大小
    max_size() 容器最大容量
    resize() 更改容器大小
    empty() 容器判空
    shrink_to_fit() 減少容器大小到滿足元素所佔存儲空間的大小
    添加函數
    push_front(const T& x) 頭部添加元素
    push_back(const T& x) 尾部添加元素
    insert(iterator it, const T& x) 任意位置插入元素
    insert(iterator it, int n, const T& x) 任意位置插入n個相同的元素
    insert(iterator it, iterator first, iterator last) 插入另一個向量的 [forst,last] 間的數據
    刪除函數
    pop_front() 頭部刪除元素
    pop_back() 尾部刪除元素
    erase(iterator it) 任意位置刪除一個元素
    erase(iterator first, iterator last) 刪除 [first,last] 之間的元素
    clear() 清空所有元素
    訪問函數
    deq[i] 下標訪問,不會檢查是否越界
    at(i) at方法訪問, at 會檢查是否越界,是則拋出 out of range 異常
    front() 訪問第一個元素
    back() 訪問最後一個元素
    其他
    assign(int nSize, const T& x) 多個元素賦初值
    swap(deque&) 交換兩個同類型容器的元素
    迭代器函數
    begin() 開始迭代器指針
    end() 末尾迭代器指針,指向最後一個元素的下一位置
    cbegin() 指向常量的開始迭代器指針,意思就是不能通過這個指針來修改所指的內容,但還是可以通過其他方式修改的,而且指針也是可以移動的。
    cend() 指向常量的末尾迭代器指針
    rbegin() 反向迭代器指針,指向最後一個元素
    rend() 反向迭代器指針,指向第一個元素的前一個元素

    參考: deque使用詳解

  • queue
    queue與stack模版非常類似,queue模版需要定義兩個模版參數,一個是元素類型,一個是容器類型,元素類型是必要的,容器類型是可選的,默認爲dqueue類型。只能在對頭刪除,隊尾添加。
    基本操作:

    功能
    push(x) 將元素x添加到隊列的末端
    pop() 彈出隊列的第一個元素,並不會返回元素的值
    front() 訪問隊首元素,並不會將其刪除
    back() 訪問隊尾元素
    size() 返回元素個數
    empty() 隊列判空,空則返回true

    queue自身不支持clear操作,有以下幾中清空隊列的方法:
    空隊賦值

    queue<T> q;
    ...
    ...
    q = queue<T> ();
    

    遍歷出隊

    while(q.empty()) q.pop();
    

    使用swap,這種是最高效的。

    void clear<queue<T>& q>
    {
        queue<T>& empty;
        q.swap(empty);//或swap(empty, q);
    }
    

    ACboy needs your help again!
    代碼:

    //使用棧和隊列模擬
    #include<bits/stdc++.h>
    using namespace std;
    
    void Queue(int n)
    {
        queue<int> q;
        while(n--)
        {
            string s;
            cin>> s;
            if(s == "IN")
            {
                int a;
                scanf("%d", &a);
                q.push(a);
            }
            else
            {
                if(q.empty()) printf("None");
                else
                {
                    printf("%d", q.front());
                    q.pop();
                }
                cout<< endl;
            }
    
        }
    }
    
    void Stack(int n)
    {
        stack<int> s;
        while(n--)
        {
            string str;
            cin>> str;
            if(str == "IN")
            {
                int a;
                scanf("%d", &a);
                s.push(a);
            }
            else
            {
                if(s.empty()) printf("None");
                else
                {
                    printf("%d", s.top());
                    s.pop();
                }
                cout<< endl;
            }
    
        }
    }
    
    
    int main()
    {
        int n;
        cin>> n;
        while(n--)
        {
            int m;
            string s;
            cin>> m>> s;
            if(s == "FIFO") Queue(m);//先入先出隊列操作
            else Stack(m);//後入先出棧操作
        }
        return 0;
    }
    
    
  • priority_queue
    優先隊列,顧名思義就是優先級高的先出隊,是隊列和排序的結合,在儲存數據的同時將數據按照設定的規則進行排序,每次push和pop後,隊列都會動態調整,將優先級最高的元素放在前面。優先隊列用二叉堆實現,push和pop一個數的複雜度都是O(log₂n)。

    定義:priority_queue<Type, Container, Functional>
    Type 就是數據類型Container 就是容器類型(Container必須是用數組實現的容器,比如vector,deque等等,但不能用 list。STL裏面默認用的是vector),Functional 就是比較的方式
    當需要用自定義的數據類型時才需要傳入這三個參數,使用基本數據類型時,只需要傳入數據類型,默認是大頂堆。

    基本操作:

    功能
    top() 訪問隊頭元素
    empty() 判斷隊列是否爲空
    size() 返回隊列內元素的個數
    push() 插入元素到隊尾,之後進行排序
    pop() 彈出隊頭元素
    swap() 交換內容

    自定義排序的方法:1.重載operator < ; 2、自己寫仿函數
    詳見:c++優先隊列(priority_queue)用法詳解

    看病要排隊
    代碼:

    #include<bits/stdc++.h>
    using namespace std;
    
    struct tmp
    {
        int priority;
        int id;
        tmp(int p, int i):priority(p), id(i){}
        bool operator <(const tmp& t) const
        {
            if(priority == t.priority)
            {
                return t.id<id;
            }
            else return priority<t.priority;
        }
    };
    
    void IN(priority_queue<tmp> &q, int p, int i)
    {
        tmp t(p,i);
        q.push(t);
    }
    
    void OUT(priority_queue<tmp> &q)
    {
        if(q.empty())
        {
            cout<< "EMPTY"<< endl;
            return ;
        }
        cout<< q.top().id<< endl;
        q.pop();
    }
    
    int main()
    {
        int c;
        while(cin>> c)
        {
            priority_queue<tmp> A, B, C;//這裏可以考慮建立一個數組q[4],可以簡化後面的case語句
            int i=0;//id
            int n, m;
            string s;
            while(c--)
            {
                cin>> s;
                if(s == "IN")
                {
                    cin>> n>> m;
                    switch(n)
                    {
                    case 1:
                        {
                            i++;
                            IN(A, m, i);
                            break;
                        }
                    case 2:
                        {
                            i++;
                            IN(B, m, i);
                            break;
                        }
                    case 3:
                        {
                            i++;
                            IN(C, m, i);
                            break;
                        }
                    }
                }
                else
                {
                    cin>> n;
                    switch(n)
                    {
                    case 1:
                        OUT(A);
                        break;
                    case 2:
                        OUT(B);
                        break;
                    case 3:
                        OUT(C);
                        break;
                    }
                }
            }
        }
        return 0;
    }
    
    
  • list
    STL的list是數據結構的雙向鏈表,內存空間可以不連續,通過指針進行數據的訪問,在任意地方的插入和刪除操作都是常數時間,非常高效。list適用於插入和刪除操作頻繁,隨機訪問較少的情景。vector與list相反,適用於隨機訪問頻繁,插入和刪除操作較少的場景。

    基本操作:

    函數 功能
    back() 返回最後一個元素
    front() 返回第一個元素
    pop_back() 刪除最後一個元素
    pop_front() 刪除第一個元素
    push_back() 在list的末尾添加一個元素
    push_front() 在list的頭部添加一個元素
    begin() 返回指向第一個元素的迭代器
    end() 返回末尾的迭代器
    erase() 刪除一個元素
    insert() 插入一個元素到list中
    unique() 刪除重複的元素

    士兵隊列訓練問題
    代碼:

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef list<int> LISTINT;
    
    int main()
    {
        int N;
        cin>> N;
        while(N--)
        {
            int n;
            LISTINT l;
            LISTINT::iterator it;
            cin>> n;
    
            for(int i=1; i<=n; i++)
            {
                l.push_back(i);//賦序號
            }
            int k=2;//出列號碼
            while(l.size()>3)
            {
                int num=1;
                for(it=l.begin(); it!=l.end(); num++)
                {
                    if(num%k == 0) it = l.erase(it);
                    else it++;
                }
    
                k==2? k=3:k=2;
            }
            for(it=l.begin(); it!=l.end(); it++)
            {
                if(it!=l.begin()) cout<< " ";
                cout<< *it;
            }
            cout<< endl;
        }
    }
    
    
  • set
    set是集合,集合中的每個元素只出現一次,並且是排好序的,訪問元素的時間複雜度是O(log₂n),效率很高。

    基本操作:
    定義 set<type> s;

    函數 功能
    insert(k) 插入元素
    erase(k) 刪除元素
    clear() 清空set
    empty() 判斷集合是否爲空
    size() 返回元素個數
    find(k) 返回一個迭代器,指向鍵值k
    lower_bound(k) 返回一個迭代器,指向第一個不小於k的元素
    upper_bound(k) 返回一個迭代器,指向第一個大於k的元素

    產生冠軍
    代碼:

    //STL - set/map
    //當且僅當只有一個人沒有輸過時,可以確認冠軍
    #include<bits/stdc++.h>
    using namespace std;
    
    int main()
    {
        int n;
        while(1)
        {
            set<string> s1, s2;
            cin>> n;
            if(n == 0) break;
            while(n--)
            {
                string a, b;
                cin>> a>> b;
                s1.insert(a);
                s1.insert(b);
                s2.insert(b);
            }
    
            if((s1.size()-s2.size()) == 1) cout<< "Yes";
            else cout<< "No";
            cout<< endl;
        }
    }
    
    
  • map
    map是關聯容器,實現從鍵(key)到值(value)的映射。詳見map用法總結(整理)
    Shopping
    代碼:

    #include<bits/stdc++.h>
    using namespace std;
    
    int main()
    {
        int n,m,p;
        map<string, int> shop;
    
        while(cin>> n)
        {
            string s;
            for(int i=0; i<n; i++) cin>> s;
            cin>> m;
            while(m--)
            {
                for(int i=0; i<n; i++)
                {
                    cin>> p>> s;
                    shop[s] += p;
                }
                map<string, int>::iterator it;
                int i=1;
                for(it=shop.begin(); it!=shop.end(); it++)
                {
                    if(it->second > shop["memory"]) i++;
                }
                cout<< i<< endl;
            }
            shop.clear();//每次結束後清空數據
        }
        return 0;
    }
    
    

2.相關函數

  • sort()
    sort()排序默認從小到大排序,時間複雜度:O(nlog₂n),排序的範圍[first,end),可以自定義比較函數排序,系統自帶的4個排序函數爲:less() ; greater() ; less_equal() ; greater_equal()

    相關函數:
    1.stable_sort()		排序元素相等時,保留原來順序
    2.partial_sort()	局部排序
    

    STL 中的 std::sort()

  • next_permutation()
    next_permutation()是STL提供的一個排列組合函數,返回值:沒有下一個排列組合時返回false,否則返回true,每次執行後會把新的排列組合(按字典順序從小到大生成新的排列組合)放到原空間中,排序範圍[first,end),可以在第三個參數位置指定比較方法。
    使用該函數時初始序列一般是一個字典順序最小的序列,可以使用sort()進行預先排序。
    Ignatius and the Princess II
    代碼:

    #include<bits/stdc++.h>
    using namespace std;
    int main()
    {
        int n, m;
        while(cin>> n)
        {
            int a[1010];
            for(int i=0; i<n; i++) a[i] = i+1;
            cin>> m;
            m -= 1;
            while(m--)
            {
                next_permutation(a, a+n);
            }
            for(int i=0; i<n; i++)
            {
                if(i!=0) cout<< " "<< a[i];
                else cout<< a[i];
            }
            cout<< "/"<< endl;
        }
        return 0;
    }
    
    

    排列2

    #include<bits/stdc++.h>
    using namespace std;
    int main()
    {
        int a[4],k=0, f=1;
        cin>> a[0]>> a[1]>> a[2]>> a[3];
        if(a[0]==0 && a[1]==0 && a[2]==0 && a[3]==0) f=0;
        while(f)
        {
            if(k) cout<< endl;
            sort(a, a+4);
            int flag, i=0;
            while(a[i]==0) i++;
            flag = a[i];
            i=0;
            do
            {
                if(a[0]!=0)
                {
                    if(a[0] != flag)
                    {
                        cout<< endl;
                        i = 0;
                    }
                    if(i) cout<< " ";
                    for(int j=0; j<4; j++) cout<<a[j];
                    i = 1;
                    flag = a[0];
                }
            }while(next_permutation(a, a+4));
            k++;
            cout<< endl;
            cin>> a[0]>> a[1]>> a[2]>> a[3];
            if(a[0]==0 && a[1]==0 && a[2]==0 && a[3]==0) f=0;
        }
        return 0;
    }
    
    

3.一點點總結

相關知識熟練掌握就不說了格式格式格式,重要的事情說三遍,注意題目格式要求。對於有空格要求的題目,可以先用一個自己喜歡的字符代替,以方便空格的檢查,確定沒問題後,提交代碼並等待ac ,ac個毛線,記得替換成空格再提交。

發佈了9 篇原創文章 · 獲贊 1 · 訪問量 1353
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章