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