• STL+优先队列+容器(加基础知识讲解)


     【vector容器简介】:

    vector向量容器不但能像数组一样对元素进行随机访问,还能在尾部插入元素,是一个种简单、高效的容器,完全可以代替数组

    值得注意的是,vector具有内存自动管理的功能,对于元素的插入和删除,可动态调整所占用的内存空间。

    使用vector向量容器,需要头文件包含“#inlcude <vector>”。vector文件在C:Prog

    ram FilesMicrosoft Visual StudioVC98Include文件夹中可以找到。

    vector容器的下标是从0开始计数的,也就是说,如果vector容器的大小是n,那么,元素的下标是0~n-1。对于vector容器的容量定义,可以事先定义一个固定大小,事后,可以随时调整其大小;也可以事先不定义,随时使用push_back()方法从尾部扩张元素,也可以使用insert()在某个元素位置前插入新元素。

    vector容器有两个重要的方法:begin()和end()。begin()返回的是首元素位置的迭代器;end()返回的是最后一个元素的下一元素位置的迭代器。

    下面介绍如何运用vector容器:

    [创建vector对象]

    创建vector对象常用的有三种方式:

    ①不指定容器的元素个数,如定义一个用来存储整型的容器:

    vector<int> v;

    ②创建时,指定容器的大小,如定义一个用来存储10个double类型元素的向量容器:

    vector<double> v(10);

    注意,元素的下标为0~9;另外,每个元素的值初始化为0.0。

    ③创建一个具有n个元素的向量容器对象,每个元素具有指定的初始值:

    vector<double> v(10,8.6);

    上述语句定义了v向量容器,共有10个元素,每个元素的值是8.6。

    [尾部元素扩张]

    通常使用push_back()对vector容器在尾部追加新元素。尾部追加元素,vector容器会自动分配新内存空间。可对空的vector对象扩张,也可对已有元素的vector对象扩张。但在vector中无push_front(),因为对vector进行push_front()会造成所有元素的迁移,不符合vector设计的初衷。

    下面看一个实例:

    将2、7、9三个元素从尾部添加到v容器中,这样,v容器中就有三个元素,其值依次2、7、9。部分代码如下:

    1 vector<int> v;  //一个存放int元素的向量,一开始里面没有元素
    2 v.push_back(2);
    3 v.push_back(7);
    4 v.push_back(9);

    [下标方式访问vector元素]

    访问或遍历vector对象是常要做的事情。对于vector对象,可以采用下标方式随意访问它的某个元素,当然,也可以以下标方式对某元素重新赋值,这点类似于数组的访问方式。

    下面的代码就是采用下标方式对数组赋值,再输出元素的值2、7、9。部分代码如下:

    vector<int> v(3);
    v[0] = 2;
    v[1] = 7;
    v[2] = 9;
    cout<<v[0]<<" "<<v[1]<<" "<<v[2]<<endl;

    [用迭代器(iterator)访问vector元素]

     1 vector<int> v(3);
     2 v[0] = 2;
     3 v[1] = 7;
     4 v[2] = 9;
     5 //定义迭代器
     6 vector<int>::iterator it;
     7 for(it=v.begin();it!=v.end();it++){
     8    //输出迭代器上的元素值
     9    cout << *it << " ";
    10 }

    STL提供三种类型的组件:容器、迭代器和算法,它们都支持泛型程序设计标准。STL设计的精髓在于,把容器(Containers)和算法(Algorithms)分开,而迭代器(iterator)是连接容器和算法的纽带,可见迭代器在STL中的重要程度。

    迭代器的作用其实相当于一个智能指针,它指向顺序容器或关联容器中的任意元素,还能遍历整个容器。可以通过operator *操作符来解指针获得数据的值,也可以通过operator ->操作符来获取数据的指针,还能够重载++,--等运算符来移动指针。

    不同的容器可能需要不同的迭代器,实际上,在STL中,为每种容器都typedef了一个迭代器,名为iterator。例如,vector<T>的迭代器类型为vector<T>::iterator(是一种随机访问迭代器)、list<T>的迭代器类型为list<T>::iterator(是一种双向迭代器)。

    例如,定义一个容器类的迭代器的方法可以是:

    容器类名<T>::iterator   变量名; 

    所以定义一个vector向量容器类的迭代器的方法如下:

    vector<T>::iterator   变量名; 

    如vector<int>::iterator it;。

    ++it表示向前移动迭代器,使其指向容器的下一个元素。而*it返回iterator指向元素的值。

        每种容器类型提供一个begin()和一个end()成员函数。begin()返回一个iterator,它指向容器的第一个元素。end()返回一个iterator,它指向容器的末元素的下一个位置。

    [元素的删除]

    insert()方法可以在vector对象的任意位置前插入一个新的元素,同时,vector自动扩张一个元素空间,插入位置后的所有元素依次向后挪动一个位置。

    要注意的是,insert()方法要求插入的位置,是元素的迭代器位置,而不是元素的下标。

    下面的部分代码输出的结果是8,2,1,7,9,3:

    vector<int> v(3);
    v[0] = 2;
    v[1] = 7;
    v[2] = 9;
    //在最前面插入新元素,元素值为8
    v.insert(v.begin(),8);
    //在第2个元素前插入新元素1
    v.insert(v.begin()+2,1);
    //在向量末尾追加新元素3
    v.insert(v.end(),3);
    //定义迭代器变量
    vector<int>::iterator it;
    for(it=v.begin();it!=v.end();it++){
       //输出迭代器上的元素值
       cout << *it << " ";
    }

    [元素的删除]

    erase()方法可以删除vector中迭代器所指的一个元素或一段区间中的所有元素。clear()方法则一次性删除vector中的所有元素。

    下面的代码演示了vector元素的删除方法:

    //删除第2个元素(即删除单个元素),从0开始计数

    v.erase(v.begin()+2);

    //删除迭代器第1到第5区间的所有元素

    //即删除一对iterator标记的一段范围内的元素

    v.erase(v.begin()+1,v.begin()+5);

    //清空向量

    v.clear();

    清空完向量后,向量中一个元素都没有,即v.size()的值为0。

    [使用reverse反向排列算法]

    reverse反向排列算法,需要定义头文件“#inlcude <algorithm>”。algorithm文件位于C:Program FilesMicrosoft Visual StudioVC98Include文件夹中。

    reverse算法可将向量中某段迭代器区间元素反向排列,部分代码如下:

    //反向排列向量的从首到尾间的元素

    reverse(v.begin(),v.end());

    [使用sort算法对向量元素排序]

    使用sort算法,需要声明头文件“#inlcude <algorithm>”。sort算法要求使用随机访问迭代器进行排序,在默认的情况下,对向量元素进行升序排列,部分如下:

    //排序,升序排列

    sort(v.begin(),v.end());

    还可以自己设计排序比较函数,然后,把这个函数指定给sort算法,那么,sort就根据这个比较函数指定的排序规则进行排序。下面的部分代码自己设计了一个排序比较函数Comp,要求对元素的值由大到小排序:

    //自己设计排序比较函数:对元素的值进行降序排列

    bool Comp(const int &a,cont int &b) 

    {

        if(a != b)  return a > b;

        else  return a > b;

    }

    在main()函数就可以用sort算法调用Comp()函数:

    //按Comp函数比较规则排序

    sort(v.begin(),v.end(),Comp);

    [向量的大小]

    使用size()方法可以返回向量的大小,即元素的个数。使用empty()方法返回向量是否为空。

    下面的部分代码演示了size()方法和empty()方法的用法:

    //输出向量的大小,即包含了多少个元素

    cout << v.size() << ednl;

    //输出向量是否为空,如果非空,则返回逻辑假,即0,否则返回逻辑真,即1

    cout << v.empty() << endl;

    优先队列简介】:

    C++的STL中提供了“优先队列”这一容器,可以快速完成这一操作。优先队列和普通的FIFO队列都定义在<queue>中,有push()和pop()过程,分别表示“往队列里加入新元素”和“从队列里删除队首元素”。唯一的区别是,在优先队列中,元素并不是按照进入队列的先后顺序排列,而是按照优先级的高低顺序排列。即pop()删除的是优先级最高的元素,而不一定是最先进入队列的元素。正因为如此,获取队首元素的方法不再是front(),而是pop()。

    定义优先队列最简单方法是priority_queue<类型名>q,它利用元素自身的“小于”操作符来定义优先级。例如,在priority_queue<int> q这样的优先队列中,先出队的总是最大的整数。使用自定义比较的方法和set类似:

     1 sturct cmp {
     2 
     3    bool operator()(const int a,const int b) //a的优先级比b小时返回true
     4 
     5        return a % 10 > b % 10;
     6 
     7    }
     8 
     9 };
    10 
    11 priority_queue<int,vector<int>,cmp> q;  //“个位数大的优先级反而小”的整数优先队列

    在Dijsktra算法中,d[i]小的值应该先出队,因此需要使用自定义比较器。在STL中,可以用greater<int>表示“大于”运算符,因此可以用priority_queue<int,vector<int>,

    greater<int> > q声明一个小整数先出队的优先队列。注意,最后两个大于号之间一定要有空格,不然会被误认为移位运算符“>>”。

    由于除了需要最小的d值之外,还要找到这个最小值对应的结点编号。解决方法是:把d值和编号“捆绑”成一个整体放到优先队列中,使得取出最小d值的同时也会取出对应的结点编号。

    STL中的pair便是专门把两个类型捆绑到一起的。可以用typedef pair<int,int>pii自定义一个pii类型,则priority_queue<pii,vector<pii>,greaeter<pii> >q就定义了一个由二元组构成的优先队列。pair定义了它自己的排序规则——先比较第一维,相等时才比较第二维,因此需要按(d[i],i)而不是(i,d[i])的方式组合。

    #include<iostream>
    #include<queue>
    #include<string.h>
    using namespace std;
    const int INF =100000000;
    const int MAXN =1000;
    const int MAXM =100000;
    int m,n;
    int first[MAXN],d[MAXN];
    int u[MAXM],v[MAXM],w[MAXM],next[MAXM];
    typedef pair<int ,int >pii;//STL中的pair便是专门把两个类型捆绑在一起。便于关联数据的输出与提取
    priority_queue<pii ,vector<pii>,greater<pii> > q;//创建优先队列,这是自己定义的优先队列排序函
    int main()
    {
       cin>>m>>n;
       for(int e=0;e<m;e++)
       {
            cin>>u[e]>>v[e]>>w[e];
            next[e]=first[u[e]];//这里是把第e条边的起点所对应的第一条边的值赋给e的下一条边
            first[u[e]]=e; //并把第e条边的起点的第一条边设置为e 
       }
       bool done[MAXN];
       for(int i=0;i<n;i++)
       d[i]=(i==0?0:INF);
       memset(done,0,sizeof(memset));
       q.push(make_pair(d[0],0));//将第一条数据压入优先队列
       while(!q.empty())
       {
           pii u=q.top();
           q.pop();
           int x =u.second;//获得节点号,u.first是d[i]
           if(done[x]) continue;
           done[x]=1;
           for(e=first[x];e!=-1;e=next[e])
           {
               if(d[v[e]]>d[x]]+w[e])
               d[v[e]]=d[x]]+w[e];
               q.push(make_pair(d[v[e]],v[e])); //如果未被访问,则压入优先队列  
           }
           
       }
       for(int i=0;i<MAXN;i++)
       cout<<d[i]<<endl;
       return 0;
    }
  • 相关阅读:
    JAVA正则表达式matcher.find()和 matcher.matches()的区别
    Mysql面试题
    Mysql常见的错误码
    关于Spring的69个问题
    Exception总结
    JDK5-8特性归纳
    TCP和UDP的区别
    log4j介绍和使用
    tomcat项目中配置数据库连接池
    Mybatis简介、环境搭建和详解
  • 原文地址:https://www.cnblogs.com/khbcsu/p/3878686.html
Copyright © 2020-2023  润新知