STL之容器(1)

STL容器类的模板

容器部分主要由头文件<vector>,<list>,<deque>,<set>,<map>,<stack>和<queue>组成。

对于常用的一些容器和容器适配器(可以看作由其它容器实现的容器),可以通过下表总结一下它们和相应头文件的对应关系。

数据结构 描述 实现头文件

向量(vector) 连续存储的元素 <vector>

列表(list) 由节点组成的双向链表,每个结点包含着一个元素<list>

双队列(deque) 连续存储的指向不同元素的指针所组成的数组 <deque>

集合(set) 由节点组成的红黑树,每个节点都包含着一个元素, <set>

节点之间以某种作用于元素对的位次排列,

没有两个不同的元素能够拥有相同的次序

多重集合(multiset) 允许存在两个次序相等的元素的集合<set>

栈(stack) 后进先出的值的排列 <stack>

队列(queue) 先进先出的执的排列 <queue>

优先队列(priority_queue) 元素的次序是由作用于所存储的值对上的某种谓词决定的一种队列 <queue>

映射(map) 由{键,值}对组成的集合,以某种作用于键对上的谓词排列 <map>

多重映射(multimap) 允许键对有相等的次序的映射 <map>

优先队列:顾名思义,首先它是一个队列,但是它强调了“优先”二字,所以,已经不能算是一般意义上的队列了,它的“优先”意指取队首元素时,有一定的选择性,即根据元素的属性选择某一项值最优的出队~

百度百科上这样描述的:

  优先级队列 是不同于先进先出队列的另一种队列。每次从队列中取出的是具有最高优先权的元素

  优先队列的类定义  

  优先队列是0个或多个元素的集合,每个元素都有一个优先权或值,对优先队列执行的操作有1) 查找;2) 插入一个新元素;3) 删除.在最小优先队列(min priorityq u e u e)中,查找操作用来搜索优先权最小的元素,删除操作用来删除该元素;对于最大优先队列(max priority queue),查找操作用来搜索优先权最大的元素,删除操作用来删除该元素.优先权队列中的元素可以有相同的优先权,查找与删除操作可根据任意优先权进行.

优先队列,其构造及具体实现我们可以先不用深究,我们现在只需要了解其特性,及在做题中的用法,相信,看过之后你会收获不少。

使用优先队列,首先要包函STL头文件"queue",

以一个例子来解释吧(呃,写完才发现,这个代码包函了几乎所有我们要用到的用法,仔细看看吧):

/*优先队列的基本使用    2010/7/24    dooder*/

#include<stdio.h>

#include<functional>

#include<queue>

#include<vector>

using namespace std;

//定义结构,使用运算符重载,自定义优先级1

struct cmp1{

bool operator ()(int &a,int &b){

return a>b;//最小值优先

}

};

struct cmp2{

bool operator ()(int &a,int &b){

return a<b;//最大值优先

}

};

//定义结构,使用运算符重载,自定义优先级2

struct number1{

int x;

bool operator < (const number1 &a) const {

return x>a.x;//最小值优先

}

};

struct number2{

int x;

bool operator < (const number2 &a) const {

return x<a.x;//最大值优先

}

};

int a[]={14,10,56,7,83,22,36,91,3,47,72,0};

number1 num1[]={14,10,56,7,83,22,36,91,3,47,72,0};

number2 num2[]={14,10,56,7,83,22,36,91,3,47,72,0};

int main()

{   priority_queue<int>que;//采用默认优先级构造队列

priority_queue<int,vector<int>,cmp1>que1;//最小值优先

priority_queue<int,vector<int>,cmp2>que2;//最大值优先

priority_queue<int,vector<int>,greater<int> >que3;//注意“>>”会被认为错误,

//这是右移运算符,所以这里用空格号隔开

priority_queue<int,vector<int>,less<int> >que4;////最大值优先

priority_queue<number1>que5;

priority_queue<number2>que6;

int i;

for(i=0;a[i];i++){

que.push(a[i]);

que1.push(a[i]);

que2.push(a[i]);

que3.push(a[i]);

que4.push(a[i]);

}

for(i=0;num1[i].x;i++)

que5.push(num1[i]);

for(i=0;num2[i].x;i++)

que6.push(num2[i]);

printf("采用默认优先关系:\n(priority_queue<int>que;)\n");

printf("Queue 0:\n");

while(!que.empty()){

printf("%3d",que.top());

que.pop();

}

puts("");

puts("");

printf("采用结构体自定义优先级方式一:\n(priority_queue<int,vector<int>,cmp>que;)\n");

printf("Queue 1:\n");

while(!que1.empty()){

printf("%3d",que1.top());

que1.pop();

}

puts("");

printf("Queue 2:\n");

while(!que2.empty()){

printf("%3d",que2.top());

que2.pop();

}

puts("");

puts("");

printf("采用头文件\"functional\"内定义优先级:\n(priority_queue<int,vector<int>,greater<int>/less<int> >que;)\n");

printf("Queue 3:\n");

while(!que3.empty()){

printf("%3d",que3.top());

que3.pop();

}

puts("");

printf("Queue 4:\n");

while(!que4.empty()){

printf("%3d",que4.top());

que4.pop();

}

puts("");

puts("");

printf("采用结构体自定义优先级方式二:\n(priority_queue<number>que)\n");

printf("Queue 5:\n");

while(!que5.empty()){

printf("%3d",que5.top());

que5.pop();

}

puts("");

printf("Queue 6:\n");

while(!que6.empty()){

printf("%3d",que6.top());

que6.pop();

}

puts("");

return 0;

}

/*

运行结果 :

采用默认优先关系:

(priority_queue<int>que;)

Queue 0:

91 83 72 56 47 36 22 14 10  7  3

采用结构体自定义优先级方式一:

(priority_queue<int,vector<int>,cmp>que;)

Queue 1:

3  7 10 14 22 36 47 56 72 83 91

Queue 2:

91 83 72 56 47 36 22 14 10  7  3

采用头文件"functional"内定义优先级:

(priority_queue<int,vector<int>,greater<int>/less<int> >que;)

Queue 3:

3  7 10 14 22 36 47 56 72 83 91

Queue 4:

91 83 72 56 47 36 22 14 10  7  3

采用结构体自定义优先级方式二:

(priority_queue<number>que)

Queue 5:

3  7 10 14 22 36 47 56 72 83 91

Queue 6:

91 83 72 56 47 36 22 14 10  7  3

*/

运行结果:

采用默认优先关系:

(priority_queue<int>que;)

Queue 0:

91 83 72 56 47 36 22 14 10  7  3

采用结构体自定义优先级方式一:

(priority_queue<int,vector<int>,cmp>que;)

Queue 1:

3  7 10 14 22 36 47 56 72 83 91

Queue 2:

91 83 72 56 47 36 22 14 10  7  3

采用头文件"functional"内定义优先级:

(priority_queue<int,vector<int>,greater<int>/less<int> >que;)

Queue 3:

3  7 10 14 22 36 47 56 72 83 91

Queue 4:

91 83 72 56 47 36 22 14 10  7  3

采用结构体自定义优先级方式二:

(priority_queue<number>que)

Queue 5:

3  7 10 14 22 36 47 56 72 83 91

Queue 6:

91 83 72 56 47 36 22 14 10  7  3

好了,如果你仔细看完了上面的代码,那么你就可以基本使用优先队列了,下面给出一些我做题中有过的一些应用,希望能给大家带来一些启

示~

1、先来一个我们最近做的题吧,http://acm.hdu.edu.cn/showproblem.php?pid=1242

题意:某人被关在囚笼里等待朋友解救,问能否解救成功,最少需要多少时间~

具体:可同时有几个朋友,每走一格消耗一分钟的时间 ,地图上还存在着卫兵,卫兵可以解决掉,但是要另外花费一分钟~

分析:从“a”出发,此题可以用回溯法进行深搜,但那样做的话,效率还是不能让人满意,但是广搜的话,由于入队后每次出队时,根据地

图情况的不同,出队元素所记忆的时间并不是层次递增的,因此使用简单广搜的话,同样需要全部搜索才能找到正确答案。有没有一种方法能

让某一步因为遇到士兵而多花时间的结点在队列中向后推迟一层出队呢?答案是肯定的,在这里我们可以用优先队列来实现,总体思想上是,

根据时间进行优先性选择,每次都要出队当前队列元素中记录时间最少的出队,而入队处理时,我们可以按顺序对四个方向上的各种情况按正

常处理入队就行了,出队顺序由优先队列根据预设优先性自动控制。这样,我们就可以从“a”进行基于优先队列的范围搜索了,并且在第一

次抵达有朋友的位置时得到正确结果~具体实现代码:

/*HDU 1242  基于优先队列的范围搜索,16ms   dooder*/

#include<stdio.h>

#include<queue>

using namespace std;

#define M 201

typedef struct p{

int x,y,t;

bool operator < (const p &a)const

{

return t>a.t;//取时间最少优先

}

}Point;

char map[M][M];

Point start;

int n,m;

int dir[][2]={{1,0},{-1,0},{0,1},{0,-1}};

int bfs()

{

priority_queue<Point>que;

Point cur,next;

int i;

map[start.x][start.y]=‘#‘;

que.push(start);

while(!que.empty()){

cur=que.top();//由优先队列自动完成出队时间最少的元素

que.pop();

for(i=0;i<4;i++){

next.x=cur.x+dir[i][0];

next.y=cur.y+dir[i][1];

next.t=cur.t+1;

if(next.x<0||next.x>=n||next.y<0||next.y>=m)

continue;

if(map[next.x][next.y]==‘#‘)

continue;

if(map[next.x][next.y]==‘r‘)

return next.t;

if(map[next.x][next.y]==‘.‘){

map[next.x][next.y]=‘#‘;

que.push(next);

}

else if(map[next.x][next.y]==‘x‘){

map[next.x][next.y]=‘#‘;

next.t++;

que.push(next);

}

}

}

return -1;

}

int main()

{

int i,ans;

char *p;

while(scanf("%d%d",&n,&m)!=-1){

for(i=0;i<n;i++){

scanf("%s",map[i]);

if(p=strchr(map[i],‘a‘)){

start.x=i;

start.y=p-map[i];

start.t=0;

}

}

ans=bfs();

printf(ans+1?"%d\n":"Poor ANGEL has to stay in the prison all his life.\n",ans);

}

return 0;

}

2、http://acm.hdu.edu.cn/showproblem.php?pid=1053

题意:给出一行字符串,求出其原编码需要的编码长度和哈夫曼编码所需的长度,并求其比值

分析:根据哈夫曼生成树的生成过程可知,其生成树的权值是固定的而且这个值是最小的,而且其值根据生成树的顺序,我们可以找出规律而

不需要真的去生成一棵树然后再求出权值,其模拟过程为取出队列中权值最小的两个元素,将其值加入结果中,然后将这两个元素的权值求和

即得出其父节点的权值,将生成元素作为结点入队~~如此循环,直至取出队列中最后两个元素加入结果,实现代码如下:

view plaincopy to clipboardprint?

/*HDU 1053  采用广搜求哈夫曼生成树的权值 0ms   dooder*/

#include<stdio.h>

#include<string.h>

#include<ctype.h>

#include<functional>

#include<queue>

using namespace std;

#define M 1000050

char str[M];

int list[27];

priority_queue< int,vector<int>,greater<int> >que;

int main()

{

int ans,sum;

int i,a,b,c;

while(scanf("%s",str),strcmp(str,"END")){

memset(list,0,sizeof(list));

for(i=0;str[i];i++){

if(isalpha(str[i]))

list[str[i]-‘A‘]++;

else

list[26]++;

}

sum=i*8;ans=i;c=0;

for(i=0;i<27;i++){

if(list[i]){

que.push(list[i]);

c++;

}

}

if(c>1){ans=0;//注意只有一种字符的情况

while(que.size()!=1){

a=que.top();

que.pop();

b=que.top();

que.pop();

ans+=a+b;

que.push(a+b);

}

while(!que.empty())//使用后清空队列

que.pop();

}

printf("%d %d %.1f\n",sum,ans,1.0*sum/ans);

}

return 0;

}

3、http://acm.pku.edu.cn/JudgeOnline/problem?id=2263

这是第二次练习赛时,我们做过的最后一题,这里采用优先队列进行实现,在《谁说不能这样做题》中已提到这种方法,在这里再次放出代

码,~

题意:给出各城市间道路的限制载重量,求出从一个城市到另外一个城市的贷车能够运载的最大货物重量。

分析:采用优先队列,每次取出当前队列中结点的minheavy最大值出队,对它的连接结点搜索入队,这样,从出发点开始就可以

在到达终点时求出结果,即最大载货物重,实现代码如下:

view plaincopy to clipboardprint?

/*POJ 2263  16ms  dooder*/

#include<stdio.h>

#include<string.h>

#include<queue>

using namespace std;

#define M 201

typedef struct w{

int city;

int mintons;

bool operator < (const w &a)const {

return mintons < a.mintons;

}//优先性定义

}Way;

char citys[M][31];

int map[M][M];

bool mark[M][M];

int n,m,from,to,ans,k;

priority_queue <Way> que;

int min(int a,int b)

{

return a>b?b:a;

}

void bfs()

{

Way cur,next;

int i;

while(!que.empty()){

cur=que.top();

que.pop();

if(cur.city==to){

if(cur.mintons>ans)

ans=cur.mintons;

while(!que.empty())

que.pop();

return ;

}

for(i=0;i<n;i++){

if(map[cur.city][i]&&!mark[cur.city][i]){

next.city=i;

next.mintons=min(cur.mintons,map[cur.city][i]);

mark[cur.city][i]=mark[i][cur.city]=1;

que.push(next);

}

}

}

}

void run()

{

int i,temp,index;

Way cur;

ans=0;

memset(mark,0,sizeof(mark));

temp=0;

for(i=0;i<n;i++){

if(map[from][i]>temp){

temp=map[from][i];

index=i;

}

}

cur.city=index;

cur.mintons=temp;

que.push(cur);

bfs();

}

int main()

{

int k1,k2,tons,t=1;

char s1[31],s2[31];

while(scanf("%d%d",&n,&m),n||m){

k=0;

while(m--){

scanf("%s%s%d",s1,s2,&tons);

for(k1=0;strcmp(s1,citys[k1])&&k1<k;k1++);

if(k1==k)

strcpy(citys[k++],s1);

for(k2=0;strcmp(s2,citys[k2])&&k2<k;k2++);

if(k2==k)

strcpy(citys[k++],s2);

map[k1][k2]=map[k2][k1]=tons;

}

scanf("%s%s",s1,s2);

for(from=0;strcmp(citys[from],s1);from++);

for(to=0;strcmp(citys[to],s2);to++);

run();

printf("Scenario #%d\n",t++);

printf("%d tons\n\n",ans);

}

return 0;

}

当然了,优先队列的用法决不是仅仅提到的这些,各种应用还需要大家去发现,给道题大家可以练习一下hdu 2066\

相信大家已经学到不少了,还有一点可以告诉大家,优先队列是启发式搜索的数据结构基础,希望好好理解,并逐步掌握其用法~

加:失策啊,竟然忘了说优先队列的效率了,其时间复杂度为O(logn).n为队列中元素的个数,存取都需要消耗时间~

时间: 2024-08-28 13:03:53

STL之容器(1)的相关文章

C++ STL vector容器学习

STL(Standard Template Library)标准模板库是C++最重要的组成部分,它提供了一组表示容器.迭代器.函数对象和算法的模板.其中容器是存储类型相同的数据的结构(如vector,list, deque, set, map等),算法完成特定任务,迭代器用来遍历容器对象,扮演容器和算法之间的胶合剂. 模板类vector 在计算中,矢量(vector)对应数组,它的数据安排以及操作方式,与array非常类似.在C++中,使用vector模板类时,需要头文件包含#include<v

基于内存查看STL常用容器内容

有时候在线上使用gdb调试程序core问题时,可能没有符号文件,拿到的仅是一个内存地址,如果这个指向的是一个STL对象,那么如何查看这个对象的内容呢? 只需要知道STL各个容器的数据结构实现,就可以查看其内容.本文描述了SGI STL实现中常用容器的数据结构,以及如何在gdb中查看其内容. string string,即basic_string bits/basic_string.h: mutable _Alloc_hider _M_dataplus; ... const _CharT* c_s

Effective STL --关联容器

高效STL-关联容器 标准关联容器中最重要的就是基于等价而不是相等.比如对于基本的函数库有find函数,但是对于set关联容器也是有find成员函数的.因为标准关联容器保持有序,所以每一个容器必须有一个定义了怎么保持东西有序的比较函数(默认是less).等价是根据这个比较函数定义的,所以标准关联容器的用户只需要为他们要使用的任意容器指定一个比较函数 必须为指针的关联容器指定比较类型 一定要明确对于一个容器来说,容器内的迭代器是存入该容器对象的指针,比如一个关联容器存入指针类型,那么使用这个容器的

STL的容器算法迭代器的设计理念

1) STL的容器通过类模板技术,实现数据类型和容器模型的分离. 2) STL的迭代器技术实现了遍历容器的统一方法:也为STL的算法提供了统一性. 3) STL的函数对象实现了自定义数据类型的算法运算 核心思想:其实函数对象本质就是回调函数,回调函数的思想,就是任务的编写者和任务的调用者有效解耦合,函数指针做函数参数. 4) 具体例子:transform算法的输入,通过迭代器first和last指向的元算作为输入:通过result作为输出:通过函数对象来做自定义数据类型的运算. 版权声明:本文为

stl string 容器的使用

string 是基本的字符串序列容器,对应数据结构中的串,和vector<char>也类似,但功能更多 string 容器的使用 1,string 的构造函数是. string() 2,string的添加函数,.   insert(),push_back() 3,string的遍历.      数组形式遍历,迭代器形式遍历 4,string的字符串替换  replace() 5,string 的字符搜索 find() 6,其他的常见函数 size(),length(),empty() #inc

c++ stl deque容器

c++中,Deque容器和vector相似,deque内部也采用动态数组来管理元素,支持随机存取..头文件<deque> 1.deque和vector的不同之处: 1)两端都可以较快速地按插元素和删除元素,而vector只能在尾端进行 2)在对内存有所限制的系统中,deque可以包含更多的元素,因为它不止一块内存.因此deque的max_size()可能更大 3)deque不支持对容量和内存的重分配时机的控制. 4)deque的内存区块不再被使用时,会被释放.deque的内存大小是可缩减的,由

STL中容器的push()或者push_back()函数的一点说明

在STL的queue 或者 vector.list等容器适配器或者容器中,会经常用到的函数就是push()或者push_back()函数,但是有一点需要明确的是: 在使用这些函数对容器/适配器对象增加新元素的时候,实际上是对原有的元素对象复制重新新建了一个元素对象作为元素压入到容器/适配器对象中. 例如: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include<queue> #include<iostream> u

顺序容器和关联容器的特点&amp;STL各容器粗略对比

顺序容器和关联容器的特点1.所有的关联容器都会自动排序,但map和set不允许重复元素,而multimap和multiset允许重复元素 2.关联容器中,map和multimap的key和val是分开的,而set和multiset的key和val是相同的 3.除了vector和deque的迭代器为随机迭代器之外,其余容器的迭代器都是双向迭代器,不能进行++操作 4.顺序容器的insert函数的三种形式为: iterator insert(iterator position, value_type

浅析常用STL中容器插入数据失败

昨天在上班的时候,碰到一个问题,关于 STL常用容器插入数据失败. 问题详细:在一个类构造函数填零后,那么map list 插入数据失败了,但是vector不会.测试代码如下: class Test { public: Test(){memset(this,0,sizeof(*this));} void InsertElement(){m_map.insert(std::make_pair(1,1)); } void InsetElement_vector(){ m_vector.push_ba

STL之容器适配器priority_queue的实现框架

说明:本文仅供学习交流,转载请标明出处,欢迎转载! 在前面的文章STL之heap相关操作算法中介绍了堆的相关操作算法,由于堆的注意主要作用是用于排序,我们也知道堆排序的时间复杂度为o(nlogn),是一种不稳定的排序算法,利用堆这一数据结构,我们可以很快第获取一个大数据中最大(或最小)的k个数.同时,上篇文章中,也提出了相关heap算法的一些问题. 问题1:在调用push_heap函数实现向堆中插入元素之前,我们必须要先将向底层容器的末端插入该元素,然后才能调用push_heap内部的向上调整来