• hzwer收集课件笔记


    C++模板与STL库介绍.ppt

    链接:https://raw.githubusercontent.com/hzwer/shareOI/master/基础算法/C%2B%2B模板与STL库介绍.ppt

    作为一个橙名玩家大部分都是会的,不过有几个新知识:

    容器:可容纳各种数据类型的数据结构。
    迭代器:可依次存取容器中元素的东西。
    算法:用来操作容器中的元素的函数模板。例如,STL用sort来对一个vector中的数据进行排序,用find来搜索一个list中的对象。
    函数本身与他们操作的数据的结构和类型无关,因此他们可以在从简单数组到高度复杂容器的任何数据结构上使用。

    只不过性能就不一定保证了,比如用algorithm::lower_bound(set1.begin(), set1.end(), value),绝对暴毙。

    容器分为三大类:
    1.顺序容器:vector,deque,list;
    2.关联容器:set,multiset,map,multimap;
    3.容器适配器,stack,queue,priority_queue。

    stack和queue确实是deque的适配器,是把deque堵住一端实现的,queue可以理解但是stack为什么要用deque实现呢?

    是为了push和pop操作是个性能更平均的O(1)(因为vector在内存不足时大量复制发生卡顿,但是deque的卡顿不明显,而且deque回收内存更方便),并且已有的元素不发生内存地址的变化,可以使得指针仍然有效(但是迭代器可能会无效)。但为什么不用list做呢?这就不懂了,或许list性能比deque要低吧。

    (参见https://blog.csdn.net/u012989012/article/details/84255664)

    priority_queue默认使用的容器是vector,这个很好理解,heap本身就是用数组做的。

    所有标准库容器共有的成员函数:
    按字典序比较两个容器大小的运算符:<, <=, >, >=, ==, !=
    empty:判断容器中是否有元素;
    max_size:容器中最多能装多少元素;
    size:容器中元素个数(注意这个一般是unsigned的,不要用这个-1或者与负数比较);
    swap:交换两个容器的内容。

    只在顺序容器和关联容器中有的成员函数:
    begin:返回指向容器中第一个元素的迭代器;
    end:返回指向容器中最后一个元素后面的位置的迭代器;
    rbegin:返回指向容器中最后一个元素的迭代器;
    rend:返回指向容器中第一个元素前面的位置的迭代器;
    erase:从容器中删除一个或几个元素;
    clear:从容器中删除所有元素。

    注意rbegin和rend是不能放进sort里的(好像是这样)。
    也就是说,容器适配器是不能够erase和clear的。

    迭代器也有常迭代器,是只能读取不能修改的,例如:vector<int>::const_iterator it=vector1.begin()

    只有顺序容器和关联容器才有迭代器。

    迭代器的访问性能是不完全一样的,只有拥有随机访问迭代器的容器才能使用排序算法。

    没有随机访问迭代器的容器,比如list还有关联容器,就不要使用排序算法了,也不要使用algorithm::lower_bound,不过关联容器可以用自带的代替。

    STL 中的迭代器按功能由弱到强分为5种:
    1.输入:Input iterators 提供对数据的只读访问。
    1.输出:Output iterators 提供对数据的只写访问。
    2.正向:Forward iterators 提供读写操作,并能一次一个地向前推进迭代器。
    3.双向:Bidirectional iterators提供读写操作,并能一次一个地向前和向后移动。
    4.随机访问:Random access iterators提供读写操作,并能在数据中随机移动。

    编号大的迭代器拥有编号小的迭代器的所有功能,能当作编号小的迭代器使用。

    所有迭代器:++p, p++
    输入迭代器:*p, p = p1, p == p1, p!= p1
    输出迭代器:*p, p = p1
    正向迭代器:上面全部
    双向迭代器:上面全部,--p, p--
    随机访问迭代器:上面全部,以及:
    p += i, p -= i
    p + i:返回指向 p 后面的第i个元素的迭代器
    p - i:返回指向 p 前面的第i个元素的迭代器
    p[i]:p 后面的第i个元素的引用
    p < p1, p <= p1, p > p1, p>= p1

    注意只有随机访问迭代器可以比较大小,所以set一般用it != set1.end()来遍历,而vector可以用it < vector1.end()

    容器,迭代器类别
    vector,随机
    deque,随机
    list,双向
    set/multiset,双向
    map/multimap,双向
    stack,不支持迭代器
    queue,不支持迭代器
    priority_queue,不支持迭代器

    find查找失败返回查找区间的终点。

    也就是说假如传入的区间终点不是end迭代器就不会是end。

    所有顺序容器都支持以下成员函数:
    front:返回容器中第一个元素的引用
    back:返回容器中最后一个元素的引用
    push_back:在容器末尾插入新元素
    pop_back:删除容器末尾的元素

    vector:最普通的容器,支持随机访问迭代器,所以支持所有的alorithm的算法

    list自带一些特有的成员函数:
    push_front:在容器前面插入新元素
    pop_front:删除容器前面的元素
    sort:排序(list自带的sort,注意list不支持alorithm::sort
    remove:删除和指定值相等的所有元素
    unique:删除所有和前一个元素相同的元素(也就是用来去重的话,建议先sort,不过这个不需要erase的吗?)
    merge:合并两个链表,并清空被合并的那个链表
    reverse:颠倒链表
    splice:在指定位置前面插入另一链表中的一个或多个元素,并在另一链表中删除被插入的元素

    deque容器:在vector的基础上支持在容器前面操作。

    set容器:insert返回一个pair,这个pair的second表示是否insert成功。

    C++的pb_ds库在OI中的应用_于纪平.pdf

    链接:https://github.com/hzwer/shareOI/blob/master/基础算法/C%2B%2B的pb_ds库在OI中的应用_于纪平.pdf

    __gnu_pbds::priority_queue

    头文件:ext/pb_ds/priority_queue.hpp

    与std::priority_queue基本相同,多了一个clear可以用。

    template <typename Value_Type,
        typename Cmp_Fn = std::less <Value_Type>,
        typename Tag = pairing_heap_tag ,
        typename Allocator = std::allocator<char> 
    >class priority_queue;
    

    Tag:表示使用的堆的类型,可以是binary_heap_tag,binomial_heap_ta,rc_binomial_heap_tag,pairing_heap_tag,thin_heap_tag。

    比std的更多功能:

    可以使用begin和end获得iterator来遍历。
    可以使用increase_key和decrease_key,插入和删除单个元素。
    可以合并。

    push:插入一个value,返回push成功后的迭代器。
    modify:传入一个迭代器和一个value进行修改(修改完成后会自动调整)。
    erase:删除一个迭代器。

    join:传入另一个priority_queue的引用,那个合并到this里,然后那个会被清空(可合并堆)。

    时间复杂度

    共5种操作:push,pop,modify,erase,join。

    pair_heap_tag:push和join为常数,其他为对数。
    binary_heap_tag:只支持push和pop,为对数。
    binomial_heap_tag:push为常数,其他为对数。
    rc_binomial_heap_tag:push为常数,其他为对数。
    thin_heap_tag:push为常数,不支持join,其他为对数。若只有increase_kay,则modify也为常数。

    比起std::priority_queue的改进

    合并的时候用pair_heap_tag?
    Dijkstra的时候用thin_heap_tag?

    是这样吗?

    测试结论1:只进行push和pop,binary_heap_tag的效率与std近似,在没有开O2的时候比没有开O2的std快,其他的惨不忍睹。

    测试结论2:进行join,binary_heap_tag的join显著优于其他,包括启发式合并的std,以及pairing_heap_tag。

    测试结论3:极端情况(边巨多)堆优化Dijkstra,pairing_heap_tag和thin_heap_tag,优于其他堆,但不及线段树。(注意binary_heap_tag是不支持修改的,没有进行测试)

    测试结论4:普通堆优化Dijkstra,pairing_heap比较好,但是还是不如线段树。

    总结论:
    不join的时候,线段树大法好。
    join的时候,随机数据或者胆子大用binary_heap_tag自带的join(非启发式),时间虽然为对数但是真的小。但是这是比赛,用pairing_heap_tag或者binomial_heap_tag比较好,效率可以达到仅次于手动的左偏树。

    也就是说,要么用可并堆,使用pairing_heap_tag,要么就直接离散化上线段树。

    __gnu_pbds::tree

    头文件:ext/pb_ds/assoc_container.hpp和/ext/pb_ds/tree_policy.hpp

    使用方式和map差不多(传入两个参数key和value),不需要第二个参数的时候,传入null_type(或者null_mapped_type,当版本比较旧时)即可(变成set,连元素都不再是pair,只是第一个参数的类型)。

    template <
        typename Key, 
        typename Mapped,
        typename Cmp_Fn = std::less <Key>,
        typename Tag = rb_tree_tag,
        template <
            typename Const_Node_Iterator, 
            typename Node_Iterator,
            typename Cmp_Fn_,
            typename Allocator_
        > class Node_Update = null_tree_node_update, 
        typename Allocator = std::allocator <char> 
    >  class tree;
    

    Tag:可以是rb_tree_tag,splay_tree_tag,ov_tree_tag之一。
    Node Update:指定为tree_order_statistics_node_update,这个tree就获得了两个新的成员函数find_by_order和order_of_key,也就是名次树

    find_by_order:找第order+1小的元素。(这个是从0开始还是从1开始的呢?)
    order_of_key:询问有多少个比它小的。

    join:把两棵值域不相交的树合并,也就是一棵完全比另一棵大,否则会抛出异常。
    split:传入一个value和一个tree的引用other,把other清空然后把比value大的值移动到other。

    自带的tree_order_statistics_node_update统计的是子树的size,可以修改为统计各种信息。

    比如查询子树的value值之和,这个很复杂,维护前缀和感觉和手动数据结构一样麻烦,先留个

    缺点:
    1、不能交换左右子树,不能完全取代splay和无旋treap用来维护序列的地位。
    2、打延迟标记很麻烦,失去了偷懒的意义。

    时间复杂度

    插入数据,然后遍历。

    ov_tree_tag居然连插入然后遍历都可以TLE。
    rb_tree_tag和splay_tree_tag分别和std的set和手动的splay性能近似。

    插入数据,然后从中查询。

    树的性能同上,但是都不如__gnu_pbds::cc_hash_table和__gnu_pbds::gp_hash_table(最快)。

    __gnu_pbds::cc_hash_table和__gnu_pbds::gp_hash_table

    头文件:ext/pb_ds/assoc_container.hpp和/ext/pb_ds/hash_policy.hpp

    用法和unorder_map差不多,支持find和operator[]。

    __gnu_cxx::rope

    关联资料:
    https://www.cnblogs.com/keshuqi/p/6257642.html
    https://www.cnblogs.com/scx2015noip-as-php/p/rope.html

    顾名思义,就是比string更强的字符“缆”,用起来非常像string,但是它可以从中间位置进行一些奇奇怪怪的操作,用来替代维护序列的平衡树。

    正常操作
    支持string的各种操作,包括用cin和cout输入输出。
    插入/添加等:

    push_back(x);     //在末尾添加x,可以是一个元素,也可以是string
    insert(pos,x);    //在pos插入x,可以是一个元素,也可以是string
    erase(pos,len);   //从pos开始删除len个
    copy(pos,len,x);  //从pos开始,到pos+len为止用x代替
    replace(pos,x);   //从pos开始换成x,可以是一个元素,也可以是string
    substr(pos,len);  //提取pos开始len个
    at(x)/[x];        //访问第x个元素
    
    string &append(const string &s,int pos,int n);
                      //把字符串s中从pos开始的n个字符连接到当前字符串的结尾(没什么用)
    a.append(b);      //直接把rope/string接在后面(没什么用)
    

    可持久化操作

    rope好用的关键是可持久化操作。

    rope<int> *f[10005];
    f[i]=new rope<int>(*f[i-1]);
    

    这个操作是飞快的,与rope的size不成线性。
    假如需要翻转序列,一般维护正反两个rope即可。

  • 相关阅读:
    解决阿里云服务器磁盘报警
    linux服务器启动报错UNEXPECTED INCONSISTENCY解决方法
    记一次gitlab添加用户收不到邮件的解决办法
    php7安装redis拓展
    centos6.5安装部署zabbix监控服务端和客户端
    centos-6.5安装部署LNMP环境
    centos6.5编译安装php7
    centos6.5新增加硬盘挂载并实现开机自动挂载
    简单快速部署samba服务器
    第177天:常用正则表达式(最全)
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12685430.html
Copyright © 2020-2023  润新知