简单常见的STL的学习

来源:互联网 发布:衣服淘宝店铺 编辑:程序博客网 时间:2024/05/16 02:24

[pre] STL 容器的分类:

标准STL序列容器:vector、string、deque和list。

标准STL关联容器:set、multiset、map和multimap。

非标准序列容器slist和rope。slist是一个单向链表,rope本质上是一“重型”string。

非标准的关联容器hash_set、hase_multiset、hash_map和hash_multimap。

(一)vector

vector 的好处就是它分配的是动态空间。

vector <int> v;

12_23

不vector 的好处远不止上述那样简单!

vector 类是以容器(Container) 模式为基准设计的,也就是说,基本上它有begin()end()size()max_size()empty() 以及swap() 这几个方法。

from wiki:

  • 访问元素的方法
    • vec[i] - 访问索引值为 i 的元素引用。 (索引值从零起算,故第一个元素是vec[0]。)
    • vec.at(i) - 访问索引值为 i 的元素的引用,以 at() 访问会做数组边界检查,如果访问越界将会抛出一个例外,这是与operator[]的唯一差异。
    • vec.front() - 回传 vector 第一个元素的引用。
    • vec.back() - 回传 vector 最尾元素的引用。
  • 新增或移除元素的方法
    • vec.push_back() - 新增元素至 vector 的尾端,必要时会进行存储器配置。
    • vec.pop_back() - 删除 vector 最尾端的元素。
    • vec.insert() - 插入一个或多个元素至 vector 内的任意位置。
    • vec.erase() - 删除 vector 中一个或多个元素。
    • vec.clear() - 清空所有元素。
  • 获取长度/容量
    • vec.size() - 获取 vector 目前持有的元素个数。
    • vec.empty() - 如果 vector 内部为空,则传回 true 值。
    • vec.capacity() - 获取 vector 目前可容纳的最大元素个数。这个方法与存储器的配置有关,它通常只会增加,不会因为元素被删减而随之减少。
  • 重新配置/重置长度
    • vec.reserve() - 如有必要,可改变 vector 的容量大小(配置更多的存储器)。在众多的 STL 实做,容量只能增加,不可以减少。
    • vec.resize() - 改变 vector 目前持有的元素个数。
  • 迭代 (Iterator)
    • vec.begin() - 回传一个Iterator,它指向 vector 第一个元素。
    • vec.end() - 回传一个Iterator,它指向 vector 最尾端元素的下一个位置(请注意:它不是最末元素)。
    • vec.rbegin() - 回传一个反向Iterator,它指向 vector 最尾端元素的。
    • vec.rend() - 回传一个Iterator,它指向 vector 的第一个元素。
看到vec.insert() - 插入一个或多个元素至 vector 内的任意位置。这一行了吗? 它支持像rope一样在特定位置插入一个或多个元素。

于是就可以用它来写平衡树的题了?

函数upper_bound()返回的在前闭后开区间查找的关键字的上界

bzoj 3224

用十几行水过不能再帅!

#include <iostream>#include <cstdio>#include <vector>using namespace std;vector <int> a;int main(){    int n; scanf("%d", &n);    for(int i = 1; i <= n; i ++){        int la, x; scanf("%d%d", &la, &x);        if(la == 1)a.insert(upper_bound(a.begin(), a.end(), x), x);        if(la == 2)a.erase(lower_bound(a.begin(), a.end(), x));        if(la == 3)printf("%d\n", lower_bound(a.begin(), a.end(), x) - a.begin() + 1);        if(la == 4)printf("%d\n", a[x - 1]);        if(la == 5)printf("%d\n", *-- lower_bound(a.begin(), a.end(), x));        if(la == 6)printf("%d\n", *upper_bound(a.begin(), a.end(), x));    }    return 0;}

常用函数总结:

upper_bound(a.begin(), a.end(), x);lower_bound(a.begin(), a.end(), x);a.insert(pos, x);a.erase(pos);a.push_back(x)


(二) set

set 做的事情是动态地维护一个排序, 支持 查找键值 为 K 的点和 删除 简直 为K 的点 (但不能 超找或删除第K大的点)(而且它所包含的元素的值是唯一的)

set<int> s1;

set<char, scmp> s2;  排序准则自定义为 scmp

begin()        ,返回set容器的第一个元素

end()      ,返回set容器的最后一个元素

clear()          ,删除set容器中的所有的元素

empty()    ,判断set容器是否为空

max_size()   ,返回set容器可能包含的元素最大个数

size()      ,返回当前set容器中的元素个数

erase(iterator)  ,删除定位器iterator指向的值

erase(first,second),删除定位器first和second之间的值

erase(key_value),删除键值key_value的值

find()  ,返回给定值值得定位器,如果没找到则返回end()。

insert(key_value); 将key_value插入到set中 ,返回值是pair<set<int>::iterator,bool>,bool标志着插入是否成功,而iterator代表插入的位置,若key_value已经在set中,则iterator表示的key_value在set中的位置。

inset(first,second);将定位器first到second之间的元素插入到set中,返回值是void.

lower_bound(key_value) ,返回第一个大于等于key_value的定位器

upper_bound(key_value),返回最后一个大于等于key_value的定位器


例:

bzoj 1208 宠物收养所

当时用splay 打怎么也得六七十行呢吧。

用set 一下子变成普及组难度。。

UserProblemResultMemoryTimeLanguageCode_LengthSubmit_Timeset版1208Accepted1408 kb264 msC++/Edit779 B2014-12-15 20:36:07splay版1208Accepted2524 kb240 msC++/Edit2013 B2014-12-02 14:30:44

对比代码长度, 占用内存, 和时间, 觉得 set 的优势真的挺大的。

#include <iostream>#include <cstdio>#include <set>#define INF 1<<31 - 1#define mod 1000000using namespace std;int n, flag = -1, ans;set <int> d;int main(){    scanf("%d", &n);    d.insert(-INF); d.insert(INF);    for(int i = 1; i <= n; i ++){        int a, b; scanf("%d%d", &a, &b);            if(a == flag || d.size() == 2)flag = a, d.insert(b);        else{            int r = *d.lower_bound(b), l = *-- d.lower_bound(b);            if(b - l <= r - b && l != -INF){ans += b - l; d.erase(l);}            else {ans += r - b; d.erase(r);}            ans %= mod;        }    } cout<<ans<<endl;    return 0;}

(三), map

map 真的很常用, 学了不下五遍了。

map 就是一个一对一 的数据映射,  它存的是一个pair,

例:

hdu 1004

虽然这题本来就不难, 但是觉得这种STL题简直是在秀下线。

#include <iostream>#include <cstdio>#include <algorithm>#include <map>#define si string, int#define smap map <si>using namespace std;smap s;inline bool cmp(pair<si> a, pair<si> b){return a.second < b.second;}int main(){    int n; while(scanf("%d", &n) && n){        s.clear(); char ch[22];         for(int i = 1; i <= n; i ++)scanf("%s", ch), s[ch] ++;        smap::iterator la = max_element(s.begin(), s.end(), cmp);        cout<<la -> first<<endl;    }    return 0;}


(四) , rope

PerformanceOperationRopeStringIndex[1]O(log n)O(1)Split[1]O(log n)O(1)Concatenate (destructive)O(log n)O(n)Concatenate (nondestructive)O(n)O(n)Iterate over each character[1]O(n)O(n)InsertO(log n)O(n)AppendO(log n)O(1) amortized, O(n) worst caseDeleteO(log n)O(n)ReportO(j + log n)O(j)BuildO(n)O(n)

上面的表格已经可以很好的说明rope和string的差别了。

rope的特点就是维护一个串, 支持log n插入和删除制定位置的字符。

fhq实现的rope

bzoj 1507

splay写这题的时候。。本机测可过bzoj莫名RE

rope写得被吓傻。。

用rope的时候,注意要多加的是:

#include <ext/rope>

using namespace __gnu_cxx (这行要记得(虽然它是下划线开头的, 但是是声明空间所以可以用))。

#include <iostream>#include <cstdio>#include <cstring>#include <string>#include <ext/rope>using namespace std;using namespace __gnu_cxx;char s[2500005], s1[22];int n, now, k;crope S;int main(){    scanf("%d", &n);    while(n --){scanf("%s", s1);        if(s1[0] == 'M')scanf("%d", &now);        if(s1[0] == 'P')now --;        if(s1[0] == 'N')now ++;        if(s1[0] == 'I'){            scanf("%d", &k); getchar();            for(int i = 0; i < k; i ++)do{s[i] = getchar();}while(s[i] == '\n');            s[k] = 0;            S.insert(now, s);         }        if(s1[0] == 'D')scanf("%d", &k), S.erase(now, k);        if(s1[0] == 'G')scanf("%d", &k), S.copy(now, k, s), s[k] = 0, puts(s);    }    return 0;}



bzoj 1269

/************转自hzwer*********************/

由于rope的底层实现,insert,erase,get都是logn的

就是翻转不行,不是自己手写的打不了标记啊!!

怎么办?

答:同时维护一正一反两个rope……反转即交换两个子串……Orz……

区间循环位移?简单,拆成多个子串连起来就好了……

区间a变b b变c c变d …… z变a? 呃……维护26个rope?

区间和?滚蛋,那是线段树的活

区间kth?sorry,与数值有关的操作rope一概不支持……

函数功能push_back(x)在末尾添加xinsert(pos,x)在pos插入xerase(pos,x)从pos开始删除x个replace(pos,x)从pos开始换成xsubstr(pos,x)提取pos开始x个at(x)/[x]访问第x个元素

/********************************************/


0 0