浅谈 pb_ds 库

大部分是在wiki搬运的,只是方便我看

简介

pb_ds 库封装了很多数据结构,比如哈希(Hash)表,平衡二叉树,字典树(Trie 树),堆(优先队列)等。

就像 vectorsetmap 一样,其组件均符合 STL 的相关接口规范。部分(如优先队列)包含 STL 内对应组件的所有功能,但比 STL 功能更多。

可以使用 begin()end() 来获取 iterator 从而遍历

可以 increase_key,decrease_key 以及删除单个元素

pbds 中的堆

申请

#include <ext/pb_ds/priority_queue.hpp>
using namespace __gnu_pbds;
__gnu_pbds::priority_queue<T,Compare,Tag,Allocator> t;

模板形参

__gnu_pbds::priority_queue<T,Compare,Tag,Allocator> t中:

  • T: 储存的元素类型

  • Compare: 提供严格的弱序比较类型

  • Tag: 是 __gnu_pbds 提供的不同的五种堆,Tag 参数默认是 pairing_heap_tag 五种分别是:

    • pairing_heap_tag:配对堆 官方文档认为在非原生元素(如自定义结构体/std :: string/pair)中,配对堆表现最好
    • binary_heap_tag:二叉堆 官方文档认为在原生元素中二叉堆表现最好,不过笔者测试的表现并没有那么好
    • binomial_heap_tag:二项堆 二项堆在合并操作的表现要优于二叉堆,但是其取堆顶元素操作的复杂度比二叉堆高
    • rc_binomial_heap_tag:冗余计数二项堆
    • thin_heap_tag:除了合并的复杂度都和 Fibonacci 堆一样的一个 tag
  • Allocator:空间配置器。

操作

  • push(): 向堆中压入一个元素,返回该元素位置的迭代器。
  • pop(): 将堆顶元素弹出。
  • top(): 返回堆顶元素。
  • size() 返回元素个数。
  • empty() 返回是否非空。
  • modify(point_iterator, const key): 把迭代器位置的 key 修改为传入的 key,并对底层储存结构进行排序。
  • erase(point_iterator): 把迭代器位置的键值从堆中擦除。

实例

#include <algorithm>
#include <cstdio>
#include <ext/pb_ds/priority_queue.hpp>
#include <iostream>
using namespace __gnu_pbds;
// 由于面向OIer, 本文以常用堆 : pairing_heap_tag作为范例
// 为了更好的阅读体验,定义宏如下 :
#define pair_heap __gnu_pbds ::priority_queue<int>
pair_heap q1;  // 大根堆, 配对堆
pair_heap q2;
pair_heap ::point_iterator id;  // 一个迭代器

int main() {
  id = q1.push(1);
  // 堆中元素 : [1];
  for (int i = 2; i <= 5; i++) q1.push(i);
  // 堆中元素 :  [1, 2, 3, 4, 5];
  std ::cout << q1.top() << std ::endl;
  // 输出结果 : 5;
  q1.pop();
  // 堆中元素 : [1, 2, 3, 4];
  id = q1.push(10);
  // 堆中元素 : [1, 2, 3, 4, 10];
  q1.modify(id, 1);
  // 堆中元素 :  [1, 1, 2, 3, 4];
  std ::cout << q1.top() << std ::endl;
  // 输出结果 : 4;
  q1.pop();
  // 堆中元素 : [1, 1, 2, 3];
  id = q1.push(7);
  // 堆中元素 : [1, 1, 2, 3, 7];
  q1.erase(id);
  // 堆中元素 : [1, 1, 2, 3];
  q2.push(1), q2.push(3), q2.push(5);
  // q1中元素 : [1, 1, 2, 3], q2中元素 : [1, 3, 5];
  q2.join(q1);
  // q1中无元素,q2中元素 :[1, 1, 1, 2, 3, 3, 5];
}

感觉大部分都从wiki上搬运的,但是我还是觉得可能和STL里的priority_queue差不多?

pbds 中的平衡树

申请

#include <ext/pb_ds/assoc_container.hpp>  // 因为tree定义在这里 所以需要包含这个头文件
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
__gnu_pbds ::tree<Key, Mapped, Cmp_Fn = std::less<Key>, Tag = rb_tree_tag,
                  Node_Update = null_tree_node_update,
                  Allocator = std::allocator<char> > 
                  tr ;

模板形参

  • Key: 储存的元素类型,如果想要存储多个相同的 Key 元素,则需要使用类似于 std::pairstruct 的方法,并配合使用 lower_boundupper_bound 成员函数进行查找
  • Mapped: 映射规则(Mapped-Policy)类型,如果要指示关联容器是 集合,类似于存储元素在 std::set 中,此处填入 null_type,低版本 g++ 此处为 null_mapped_type;如果要指示关联容器是 带值的集合,类似于存储元素在 std::map 中,此处填入类似于 std::map<Key, Value>Value 类型
  • Cmp_Fn: 关键字比较函子,例如 std::less<Key>
  • Tag: 选择使用何种底层数据结构类型,默认是rb_tree_tag
    __gnu_pbds提供不同的三种平衡树,分别是:
    • rb_tree_tag:红黑树,一般使用这个,后两者的性能一般不如红黑树
    • splay_tree_tag:splay 树
  • Node_Update:用于更新节点的策略,默认使用 null_node_update,若要使用 order_of_keyfind_by_order 方法,需要使用 tree_order_statistics_node_update
  • Allocator:空间分配器类型

操作

  • insert(x):向树中插入一个元素 x,返回 std::pair<point_iterator, bool>
  • erase(x):从树中删除一个元素/迭代器 x,返回一个 bool 表明是否删除成功。
  • order_of_key(x):返回 x 以 Cmp_Fn 比较的排名。
  • find_by_order(x):返回 Cmp_Fn 比较的排名所对应元素的迭代器。
  • lower_bound(x):以 Cmp_Fn 比较做 lower_bound,返回迭代器。
  • upper_bound(x):以 Cmp_Fn 比较做 upper_bound,返回迭代器。
  • join(x):将 x 树并入当前树,前提是两棵树的类型一样,x 树被删除。
  • split(x,b):以 Cmp_Fn 比较,小于等于 x 的属于当前树,其余的属于 b 树。
  • empty():返回是否为空。
  • size():返回大小。

实例

// Common Header Simple over C++11
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<int, int> pii;
#define pb push_back
#define mp make_pair
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
__gnu_pbds ::tree<pair<int, int>, __gnu_pbds::null_type, less<pair<int, int> >,
                  __gnu_pbds::rb_tree_tag,
                  __gnu_pbds::tree_order_statistics_node_update>
    trr;

int main() {
  int cnt = 0;
  trr.insert(mp(1, cnt++));
  trr.insert(mp(5, cnt++));
  trr.insert(mp(4, cnt++));
  trr.insert(mp(3, cnt++));
  trr.insert(mp(2, cnt++));
  // 树上元素 {{1,0},{2,4},{3,3},{4,2},{5,1}}
  auto it = trr.lower_bound(mp(2, 0));
  trr.erase(it);
  // 树上元素 {{1,0},{3,3},{4,2},{5,1}}
  auto it2 = trr.find_by_order(1);
  cout << (*it2).first << endl;
  // 输出排名 0 1 2 3 中的排名 1 的元素的 first:1
  int pos = trr.order_of_key(*it2);
  cout << pos << endl;
  // 输出排名
  decltype(trr) newtr;
  trr.split(*it2, newtr);
  for (auto i = newtr.begin(); i != newtr.end(); ++i) {
    cout << (*i).first << ' ';
  }
  cout << endl;
  // {4,2},{5,1} 被放入新树
  trr.join(newtr);
  for (auto i = trr.begin(); i != trr.end(); ++i) {
    cout << (*i).first << ' ';
  }
  cout << endl;
  cout << newtr.size() << endl;
  // 将 newtr 树并入 trr 树,newtr 树被删除。
  return 0;
}
posted @ 2024-08-23 14:17  tyccyt  阅读(1)  评论(0编辑  收藏  举报