STL介绍与习题

内容参考书籍《算法笔记》

1.vector

  在一些用普通数组可能会超内存的情况下,可以选择用vector来存储数据,vector可以理解为“长度可以根据需要改变的数组”。并且用vector比较节省空间。

  使用vector,需要添加头文件#include<vector> , 而且还得在头文件下面加上using namespace std;

  (1). vector的定义

  vector<typename> name;   //单独定义一个vector ,相当于一维数组name[SIZE].  typename表示数据类型

  vector<vector<int> >     //如果typename也是一个STL容器,定义的时候记得在>>符号之间加上空格,因为c++11之前标准的编译器会把它视为移位操作。

  vector<typename> v[SIZE];   //vector数组的定义,v[0]~v[SIZE - 1]中每一个都是一个vector容器

  (2).元素的访问

  一般有两种访问方式 :通过下标访问或者通过迭代器访问。

  通过下标访问 : 和普通数组一样。

  通过迭代器访问 : 迭代器(iterator)可以理解为一种类似指针的东西。其定义是:vector<typename>::iterator it;

     这样it就是迭代器,并且可以用*it来访问vector里的元素

   如:

    for(int i = 0 ; i < 5 ;i ++ )  printf("%d", *(it + i));

  (3).vector 的常用函数分析

  push_back()  //在vector后面添加一个元素,如push_back(x);   时间复杂度是O(1)。

  pop_back()   //删除vector的尾元素。 时间复杂度是O(1)。

  size()       //获取vector中元素个数,时间复杂度O(1)。

  clear()   //清空vector中的所有元素,时间复杂度是O(N),N是vector中元素的个数。

     如:

    #include<iostream>

    using namespace std;

    int main(){

    vector<int>v;

    for(int i = 1 ; i <= 3 ; i++) vi.push_back(i);

    vi.clear();

    cout << vi.size();

    return 0;

    }

      输出结果:0

  insert()  //  insert(it , x) 用来向vector的任意迭代器it处插入一个元素x,时间复杂度O(N)。

  erase()  //有两种用法:删除单个元素、删除一个区间内的所有元素。时间复杂度都为O(N)。

    erase  (it)      //  删除迭代器为it处的元素。

    erase  (first , last) // 删除[first , last)内的所有元素

  (4).vector常见用途

    1.作为数组

    2.用邻接表存储图

例题:http://acm.hdu.edu.cn/showproblem.php?pid=4841

AC代码:

#include<iostream>

#include<vector>

using namespace std;

int n , m ;

vector<int>v;

const int N = 1e6 + 10;

int res[N];

int main(){

    while(cin >> n >> m){


        memset(res,1,sizeof(res));
        v.clear();
        for(int i = 0 ; i < 2*n ; i++)
            v.push_back(i);

        for(int i = 0 ,pos = 0 , num = 2 * n ; i< n ;i++){

            pos = (pos + m - 1) % num ;

            num --;

            res[v[pos]] = 0;

            v.erase(v.begin() + pos);
        }

        for(int i = 0 ; i< 2 * n;i++){

            if((i%50 == 0) && (i != 0)) printf(" ");

            if(res[i]) printf("G");

            else printf("B");

        }
       printf(" ");
    }
    return 0;
}

2.stack

  后进先出。#include<stack>并且用using namespace std;

(1)在STL中栈只能通过top()来访问栈顶元素

(2)常用函数

  push(x) //将x入栈,时间复杂度O(1)

  top()  //获得栈顶元素,时间复杂度O(1)

  pop() //弹出栈顶元素,时间复杂度O(1)

  empty() //检测栈是否为空,返回true为空,返回false为非空,时间复杂度O(1)

  size() //返回栈中元素个数,时间复杂度O(1)

(3)常见用途

  可以用栈来模拟实现一些递归算法,应用于DFS

例题:http://acm.hdu.edu.cn/showproblem.php?pid=1237

AC代码:

#include<iostream>

#include<stack>

using namespace std;

int n;

char c;

int main(){

    scanf("%d",&n);

    getchar();

    while(n --){

        stack<char>s;
        while(1){
            c = getchar();
            if(c==' '||c==' '||c==EOF){
                while(!s.empty()){
                    printf("%c",s.top());
                    s.pop();
                }
                if(c==' '||c==EOF)break;
                printf(" ");
            }
            else
                s.push(c);
        }
        printf(" ");
    }
    return 0;
}

3.queue   先进先出

(1)在STL中只能通过front()来访问队首元素,或是通过back()来访问队尾元素。

(2)常用函数

  push(x) //将x进行入队,时间复杂度为O(1)

  front()、 back() //分别获得队首元素和队尾元素,时间复杂度为O(1)

  pop() //令队首元素出队,时间复杂度为O(1)

  empty() //检测队列是否为空,返回true为空,返回false为非空,时间复杂度O(1)

  size() //返回栈中元素个数,时间复杂度O(1)

(3)常见用途

  当需要实现BFS时,可以不用自己动手实现一个队列,而是用queue代替。

例题:http://acm.hdu.edu.cn/showproblem.php?pid=1702

#include<iostream>
#include<stack>
#include<queue>
#include<string>
using namespace std;
int t;  //次数
int main(){
    cin >> t;
    while(t --){
        stack<int> P;
        queue<int> Q;
        string s , s1;
        int n;
        int d;
        scanf("%d",&n);
        cin >> s ;
        if(s == "FIFO"){
            for(int i = 0; i<n;i++){
                cin >> s1;
                if(s1 == "IN"){
                    scanf("%d",&d);
                    Q.push(d);
                }
                if(s1 == "OUT"){
                    if(Q.empty())  printf("None ");
                    else{
                        cout << Q.front() << endl;
                        Q.pop();
                    }
                }
            }
        }
        if(s == "FILO"){
            for(int i = 0; i<n;i++){
                cin >> s1;
                if(s1 == "IN"){
                    scanf("%d",&d);
                    P.push(d);
                }
                if(s1 == "OUT"){
                    if(P.empty())  printf("None ");
                    else{
                        cout << P.top() << endl;
                        P.pop();
                    }
                }
            }
        }
    }
    return 0 ;
}
4.priority_queue 优先队列
  队首元素一定是当前队列中优先级最高的那个。和队列不一样的是优先队列没有front()和back()函数,只能通过top()函数来访问队首元素。
 
(1)常用函数
  push(x)   //将x入队,时间复杂度为O(logN),N为当前优先队列中的元素个数。
  top()  //获得队首元素,时间复杂度O(1)
  pop()  //令队首元素出队,时间复杂度为O(logN),N为当前优先队列中的元素个数。
  empty() //检测优先队列是否为空,返回true则空,返回false则非空,时间复杂度O(1)。
  size()  //返回队列中元素的个数,时间复杂度O(1)。
 
(2)priority_queue内元素优先级设置
  1.基本数据类型的优先级设置
       优先队列对它们的优先级设置一般是数字大的优先级越高。如果是char型,则是字典序最大的。对基本数据类型来说下面两种是等价的:
    1.priority_queue<int> q;
    2.priority_queue<int , vector<int> , less<int > > q;  //less<int>表示数字大的优先级越大,而greater<int>表示数字小的优先级越大。
  2.结构体优先级设置
       如:     struct fruit{
        string name;
        int price;
        
        friend bool operator < (fruit f1 , fruit f2){
          return f1.price < f2.price;
        }
       }
    表示对操作符“<”进行了重载(重载大于号会编译错误,因为从数学上来说只需要重载小于号,即f1 > f2 等价于判断f2 < f1 , 而f1 == f2 等价于判断!(f1 < f2) && !(f2 < f1) )
    重载后小于号还是小于号的作用,此时内部就是以价格高的水果优先级高。
    
    同理,如果想以价格低的水果优先级高,只需要把return中的小于号改为大于号,即:
    
      struct fruit{
        string name;
        int price;
        
        friend bool operator < (fruit f1 , fruit f2){
          return f1.price > f2.price;   //把小于号重载为大于号
        }
       }
    最后,如果结构体内数据较为庞大,建议使用引用来提高效率,此时比较类的参数中需要加上“const”和“&”如:
    
      struct fruit{
        string name;
        int price;
        
        friend bool operator < (const fruit &f1 ,const fruit &f2){
          return f1.price > f2.price;   //把小于号重载为大于号
        }
       }
  3.priority_queue的常见用途
    可以解决一些贪心问题,也可以对Dijkstra算法进行优化(因为优先队列的本质是堆)。
    有一点需要注意,使用top()函数前,必须要empty()判断队列是否为空,否则可能因为队空而出现错误。
例题:http://acm.hdu.edu.cn/showproblem.php?pid=1873
AC代码:
#include<iostream>
#include<queue>
#include<string>
using namespace std;
struct node{
    int id ;
    int yxj ;
    friend bool operator < (const node &a,const node &b){  //保证B的优先级大于A
        if(a.yxj == b.yxj)  return a.id > b.id;
        return a.yxj < b.yxj;
    }
}N[1010];
priority_queue<node> doctor1;
priority_queue<node> doctor2;
priority_queue<node> doctor3;
int main(){
    int t , n , i , k;
    string s;
    while(scanf("%d",&n) != EOF){
        k = 1 ;
        for( i = 0 ; i < n ;i++){
            cin >> s;
            if(s == "IN"){
                N[i].id = k++;
                scanf("%d%d",&t,&N[i].yxj) ;
                if(t == 1) doctor1.push(N[i]);
                else if(t == 2)doctor2.push(N[i]);
                else if(t == 3)doctor3.push(N[i]);
            }
            else if(s == "OUT"){
                scanf("%d",&t);
                if(t == 1){
                    if(doctor1.empty()) cout<<"EMPTY"<<endl;
                    else{
                        cout << doctor1.top().id << endl;
                        doctor1.pop();
                    }
                }
                else if(t == 2){
                    if(doctor2.empty()) cout<<"EMPTY"<<endl;
                    else{
                        cout << doctor2.top().id << endl;
                        doctor2.pop();
                    }
                }
                else if(t == 3){
                    if(doctor3.empty()) cout<<"EMPTY"<<endl;
                    else{
                        cout << doctor3.top().id << endl;
                        doctor3.pop();
                    }
                }
            }
        }
    }
    return 0;
}
5.list

链表,高效率地在任意地方删除和插入,常数时间。

  常用操作与vector类似。

例题:http://acm.hdu.edu.cn/showproblem.php?pid=1276

AC代码:

#include<iostream>
#include<list>
using namespace std;
int main()
{
    int t,n;
    cin>>t;
    while(t --)
    {
        cin >> n;
        int k = 2;
        list<int> l;
        list<int>::iterator it;
        for (int i = 1; i <= n; ++ i) l.push_back(i);
        while(l.size() > 3)
        {
            int num = 1;
            for (it = l.begin();it != l.end();)
            {
                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;
    }
    return 0;
}
6.set
  1.set的定义
    翻译为集合,是一个内部自动有序且不含重复元素的容器。set自动递增排序且自动去除了重复元素。需要用头文件#include<set> 和using namespace std;
  2.元素的访问
    set只能通过迭代器iterator访问
  3.常用函数
    1.insert(x)   //将x插入到set中,并自动递增排序和去重,时间复杂度为O(logN)
    2.find(value)  //返回set中对应值为value的迭代器,时间复杂度O(logN)
    3.erase()  //同vector 1.s.erase(it) //it为删除元素的迭代器,时间复杂度O(1)
         2.s.erase(value) //value是删除元素的值。时间复杂度O(logN)   
    4.size()
    5.clear() //时间复杂度O(N)  N为元素个数
例题:http://acm.hdu.edu.cn/showproblem.php?pid=2094
AC代码:
#include <iostream>
#include<set>
using namespace std;
int main()
{
    set<string>a,b; //a存比赛人名,b存失败人名
    string s1,s2;
    int n;
    while(cin >> n && n)
    {
        for (int i = 0; i < n; ++i)
        {
            cin>>s1>>s2;
            a.insert(s1);
            a.insert(s2);
            b.insert(s2);
        }
        if (a.size()-b.size()==1) cout<<"Yes"<<endl;  //如果参赛人数比失败人数多一,那么比赛有一个人没失败,则必然是冠军
        else cout<<"No"<<endl;
        a.clear();b.clear();
    }
    return 0;
}
7.map
  map翻译为映射,定义int a[100]数组时,实际上是定义了一个int型到int型的数组,如a[0] = 25,是将0映射到25.
  
  如果要用数组来表示“字符串->页码”这样的对应关系,就可以用到map。
 
  map可以将任何基本类型(包括STL容器)映射到任何基本类型(包括STL容器)。
 
  1.map的定义
    需用到#include<map>;和using namespace std;
    map<typename1 , typename2> mp;  实现从映射前类型 键(key)到映射后类型 值(value)的映射。
       如果是字符串到整形的映射,必须用string而不能用char数组。
  2.map容器内元素的访问
    1.通过下标访问
      和访问普通数组一样,如对一个定义为map<char , int> mp的map来说 ,就可以直接mp['c'] = 20来访问20。键是唯一的。
    2.通过迭代器访问
      map迭代器的定义: map<typename1 , typename2>::iterator it;
      map迭代器是使用方式和其他STL容器不同,因为map的每一对映射都有两个typename,这就决定了必须能通过一个it来同时访问键和值。所以可以用it->first来访问键,it->second来访问值。
      map会以键从小到大的顺序自动排序,这点和set一样
    3.常用函数
      1.find(key)  //返回键为key的映射的迭代器,时间复杂度为O(logN).
      2.erase()
        1.删除单个元素   
          mp.erase(it),it为需要删除的元素的迭代器,时间复杂度O(1)
          mp.erase(key) , key为想要删除的映射的键,时间复杂度O(logN)
        2.删除一个区间内的所有元素
          mp.erase(first , last),其中first是需要删除区间的歧视迭代器,last是需要删除的区间的末尾迭代器的下一个迭代器,也是左闭右开。时间复杂度O(last - first)
      3.size()  //映射对数
      4.clear()  //清空map中的所有元素,时间复杂度O(logN)
例题:http://acm.hdu.edu.cn/showproblem.php?pid=2648
AC代码:
#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;
            }
            int rank = 1;
            map<string,int>::iterator it;
            for (it=shop.begin();it != shop.end(); ++it) if(it->second>shop["memory"]) rank++;
            cout<<rank<<endl;
        }
        shop.clear();
    }
    return 0;
}
 
8.sort()
  排序函数
  定义形式
    sort(首元素地址(必填),尾元素地址的下一个地址(必填),比较函数(非必填))  //默认为递增排序
    比较函数写法:1.基本数据类型的排序  ,   例子,实现递减排序。
      bool cmp(int a, int b){
        return a > b;  //可以理解为当a > b时把a放在前面
      }
           2.结构体数组的排序。
            也是写一个比较函数,来规定按怎样的形式排序。
           3.容器的排序    
            和基本数据类型的数组的写法大同小异。
 
9.next_permutation()
  给出一个序列在全排列中的下一个序列。
例题:求全排列
int main(){
  
  int a[3] = {1 , 3 , 2};
  sort(a, a+ 3);
  do{
    for(int i = 0 ; i<3 ; i++){
      printf("%d",i);
      printf(" ");
                }
  }while(next_permutation(a , a + 3));
  return 0;
}
 
 
啊啊啊啊啊啊阿,可算写完了,希望对大家有帮助,对自己有帮助!!!
 
 
 

 

        

  

原文地址:https://www.cnblogs.com/ZhaoHaoFei/p/11943106.html