📜  有序集和GNU C++ PBDS

📅  最后修改于: 2021-04-17 10:32:16             🧑  作者: Mango

先决条件:具有STL和Sets Data结构的基本知识。

关于订购的安排

有序集是g ++中基于策略的数据结构,可将唯一元素保持在已排序的顺序中。它以log(n)复杂度执行由STL中的set数据结构执行的所有操作,还以log(n)复杂度执行两个附加操作。

  • order_of_key(k) :严格小于k的项目数。
  • find_by_order(k) :集合中的第K个元素(从零开始计数)。

实现有序集所需的头文件及其描述

为了实现ordered_set,GNU C++库包含其他基于Policy的数据结构,我们需要包括:

第一个用来包含关联的容器或模板组,例如set,multimap,map等。此标头文件中提供了下面将要使用的基于树的数据结构。
第二个头文件用于包含tree_order_statistics_node更新,如下所述:

using namespace __gnu_pbds;

它是基于GNU的基于Policy的数据结构所必需的命名空间。

基于树的容器具有具体的结构,但是有序集实现所需的必要结构是:

tree < int ,  null_type ,  less ,  rb_tree_tag ,  tree_order_statistics_node_update >
  1. int :是我们要插入的数据类型(KEY),可以是整数,浮点数或成对的int等。
  2. null_type :这是映射的策略。此处将其用作集合为null。如果我们要获取map而不是集合,则必须使用第二个参数类型为mapd type。
  3. less :这是比较两个功能的基础。
  4. rb_tree_tag :使用的树的类型。通常是红黑树,因为它需要log(n)的时间来进行插入和删除,而其他时间则需要线性时间,例如splay_tree。
  5. tree_order_statistics_node__update :它包含在tree_policy.hpp中,并且包含用于更新基于树的容器的节点变量的各种操作,因此我们可以跟踪元数据,例如子树中的节点数

有序集合中除集合外的其他功能

与集合的先前操作一起,它支持两个主要的重要操作

  • find_by_order(k) :它在O(logn)时间中返回集合中第k个元素(从零开始计数)的迭代器。要找到第一个元素k必须为零。
    让我们假设我们有一个集合s:{1、5、6、17、88},然后:
    *(s.find_by_order(2)):集合中的第三个元素,即6
    *(s.find_by_order(4)):集合中的第5个元素,即88
  • order_of_key(k) :它返回在O(logn)时间内严格小于我们的项目k的项目数。
    让我们假设我们有一个集合s:{1、5、6、17、88},然后:
    s.order_of_key(6):严格小于6的元素数为2。
    s.order_of_key(25):严格小于25的元素数为4。

集合与有序集合之间的差异
集合和有序集合之间没有太大的区别,尽管有序集合可以假定为集合的扩展版本,能够执行在竞争性编程中广泛使用的一些更高级的功能(如上所述)。


注意ordered_set在此处用作给tree 的宏。因此,它可以被命名为ordered_set以外的任何宏名称,但是在竞争性编程领域中,通常将其称为ordered set,因为它是具有附加操作的集合。


实际应用:
假设有一种情况,即元素在数组中一个接一个地插入,并且在每次插入之后,给定范围[l,r],我们必须确定数组中大于等于l且小于等于l的元素数比等于r。最初,该数组为空。
例子:

Input :    5
           1 2
           1
           2 5
           2
           1 5

Output :   0
           1
           3

解释:

    最初,数组为空
  • 插入了5个。
  • 大于等于1且小于等于2的元素计数为0。
  • 插入1。
  • 大于等于2且小于等于5的元素计数为1,即5。
  • 2已插入。
  • 大于等于1且小于等于5的元素数为3,即5、1、2。
Input :     1
            1 2
            2
            3 5
            5
            1 4
Output :    1
            0
            2

    最初,数组为空

  • 插入1。
  • 大于等于1且小于等于2的元素计数为1,即1。
  • 2已插入。
  • 大于等于3且小于等于5的元素计数为0。
  • 插入了5个。
  • 大于等于1且小于等于4的元素计数为2,即1、2。

    如果我们在STL中使用set在set上查找upper_bound,则它仅给出元素的地址,并且我们只能使用解引用运算符(*)来找到该地址处的值。

    假设我们有一个{0,1,2,7,8,20}的集合,并且发现upper_bound为2,那么它返回一个与元素在集合中的位置相对应的地址(在本例中为7),不能减去集合的起始地址(s.begin())来查找小于2的元素数量(如矢量)。
    因此,这里需要有序集。

    注意可以借助其他一些逻辑和数据结构来实现上述功能,但是使用有序集可使代码紧凑,并且可以轻松,快速地实现。

    执行有序集

    // C++ program to demonstrate the
    // ordered set in GNU C++
    #include 
    using namespace std;
      
    // Header files, namespaces,
    // macros as defined above
    #include 
    #include 
    using namespace __gnu_pbds;
      
    #define ordered_set tree, rb_tree_tag,tree_order_statistics_node_update>
      
    // Driver program to test above functions
    int main()
    {
        // Ordered set declared with name o_set
        ordered_set o_set;
      
        // insert function to insert in
        // ordered set same as SET STL
        o_set.insert(5);
        o_set.insert(1);
        o_set.insert(2);
      
        // Finding the second smallest element
        // in the set using * because
        //  find_by_order returns an iterator
        cout << *(o_set.find_by_order(1)) 
             << endl;
      
        // Finding the number of elements
        // strictly less than k=4
        cout << o_set.order_of_key(4) 
             << endl;
      
        // Finding the count of elements less 
        // than or equal to 4 i.e. strictly less
        // than 5 if integers are present
        cout << o_set.order_of_key(5) 
             << endl;
      
        // Deleting 2 from the set if it exists
        if (o_set.find(2) != o_set.end())
            o_set.erase(o_set.find(2));
      
        // Now after deleting 2 from the set
        // Finding the second smallest element in the set
        cout << *(o_set.find_by_order(1)) 
             << endl;
      
        // Finding the number of
        // elements strictly less than k=4
        cout << o_set.order_of_key(4) 
             << endl;
      
        return 0;
    }
    

    输出

    2
    2
    2
    5
    1
    

    因此,我们现在可以轻松解决上述问题,即可以通过以下方式找到介于l和r之间的元素数:
    o_set.order_of_key(r + 1)– o_set.order_of_key(l)

    注意:由于该集合仅包含UNIQUE元素,因此,要对具有重复元素的数组执行操作,我们可以将KEY视为一对元素,而不是整数,其中第一个元素是我们数组的必需元素,而只有整数该对中的第二个元素必须是唯一的,以便整个对都是唯一的。

    有关更多详细信息,请参阅:
    https://gcc.gnu.org/onlinedocs/libstdc++/manual/policy_data_structures.html