zoukankan      html  css  js  c++  java
  • 探究STL 优先队列的自定义比较方式与 sort() 等泛型算法的自定义比较方式的区别

    前言


      

      最近在刷算法题,常常需要自定义比较函数作为作为函数对象送入 stl 中,遇到了下面的问题:

       泛型算法 sort() 实现从小到大的递增关系是这么写: 

    //sort 函数的模板声明
    // 可以看出,排序要求容器支持随机访问迭代器,类似于数组的那种下标偏移访问
    // 这里 _Compare 是类型, __comp 是实例,调用 sort 需要传入的就是 __comp 实例
    template <class _RandomAccessIter, class _Compare>
    inline void sort(_RandomAccessIter __first, _RandomAccessIter __last,
                     _Compare __comp)
    
    //利用priority_queue 实现元素间关系为递增(实现最小堆) 的方法如下:
    //自定义比较函数,排序时传入函数指针,编译器会进行类型推导做模板特化 
    bool cmp (const T& a, const T& b) { return a.x < b.x; } 
    //对于自定义结构体,可重载 < 运算符,排序时会默认调用 operator < 
    struct T{
        friend bool operator < (const T& a) { 
            return this.x < a.x; 
        } 
    };

      而优先队列里是这么写:

    // priority_queue 实现使用的默认比较是 operator< ,是最大堆数据结构,即队列头元素值最大
    template <class _Tp,
              class _Sequence __STL_DEPENDENT_DEFAULT_TMPL(vector<_Tp>),
              class _Compare
              __STL_DEPENDENT_DEFAULT_TMPL(less<typename _Sequence::value_type>)
    class priority_queue;  // 注意点:如果传入自己的仿函数,那么第二个存储类型也要传入
    //利用priority_queue 实现元素间关系为递增(实现最小堆) 的方法如下: //仿函数 struct cmp {   bool operator () (const T& a, const T& b) const { return a.x > b.x; } } //自定义结构体中重载 < struct T { Type_2 x; friend bool operator < (const T&a, const T& b) { return a.x > b.x; } }

      同样是实现元素的递增关系即 “前一元素的x分量 < 后一元素的x分量”,为什么前一个是 “a.x < b.x”,而后一个是"a.x > b.x" 呢?

    分析 sort() 


      先拿 sort() 进行分析,排序类的泛型算法很多,它们的 cmp 写法是相似的,这里先看一份冒泡排序实现递增关系如 <1, 2, 3 ..> 的写法,传入的 cmp 将特化为 Compare 实例 comp ,使得容器中的相邻元素按 cmp 中的逻辑进行排序。

    #include <iostream> 
    
    template <class BidirectionalIt, class Compare> //双向迭代器
    void bubble_sort(BidirectionalIt first, BidirectionalIt last, Compare comp) {
        for (; first != last; --last) 
            for (BidirectionalIt current = first, next = first; ++next != last; ++current) 
                if (!comp(*current, *next)) 
                    std::swap(*current, *next); } 
    }
    bool cmp (int a, int b){
        return a < b;
    }    

      一言以蔽之,就是元素将按照 cmp 中定义的逻辑关系进行排列。

      虽然 sort() 是用“快排+堆排+插入”实现的而并非冒泡,但是原理是一样的。

    分析 priority_queue 


      

      现在我们再来看看优先队列。

      STL优先队列模板:priority_queue<Type, Container, Functional> ,参数分别对应的是“元素类型”、“容器类型”、“比较函数”。

      注意,优先队列不是 STL 的容器,而是由 底层容器 vector/deque 实现的模板类,缺省情况下它利用 MAX-HEAP 实现,而 MAX-HEAP 是 vector 实现的完全二叉树,这样的东西称为适配器 (adapter)。

      在优先队列中默认优先级高的元素先出队列,具体操作是使用函数对象 less<> 重载了 “<”,所以这里优先级的高低是由 “<” 确定的,所以当我们按下面的形式写:

    priority_queue<int> pQueue;

      通过操作符 < 知道元素关键字大的优先级高,此时是最大堆。若每次从堆顶取一个元素,最后得到一个序列,那么序列中元素的关系是递减的。

      我们想要让元素的关系是递增该怎么做?

       第一种办法:使用与 less<>作用相反的函数对象 greater<> 。

        ex:如下

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

      

      第二种办法:在自定义结构体中重载  < ,通过它实现对自定义数据类型的优先级的定义。

        ex:通过重载 operator < 操作符以比较元素的优先级

    struct T{
        DataType key;
        friend bool operator < (const T& a, const T& b) {
            return a.key > b.key;
        }
    };

        逻辑是这样:假设 a.key > b.key 为 TRUE ,由于默认是最大堆,此时优先队列会认为 a < b ,即 b 的优先级比 a 高,所以 b会被先出队,这样就实现了关键字小的元素先出队。最后输出的序列是按关键字递增的。

        但此时不能像基本类型这样声明 :priority_queue<T, vector<T>, greater<T> > (注意空格);

        而是只能这样声明:priority_queue<T>

        原因是 greater<T> 没有定义,如果想用这种方法定义,则使用方法3。

        注意,这种办法有缺点,如果有两个模板容器对同一个自定义数据结构需要不同的比较器,那么办法就不如函数对象适用了。

      

      第三种办法:通过函数对象以定义元素的优先级。

      ex:如下

    struct T {
        int x;
        T(int xx) : x(xx) {}
    };
    
    struct cmp{
        bool operator () (const T& a, const T& b) const
      {
    return a.x > b.x; } };

        逻辑和 2 是一样的。这里重载了 () 的目的是使得 cmp 成为像 less<> 一样的函数对象,作为新的比较比较级的规则以 priority_queue 第三个参数的形式传入 priority_queue 中。

       

    总结


      优先队列的“比较级”概念与”由MAX-HEAP实现“这一特性,使得自定义比较函数中需要这么写: "a.x>b.x" 。

    延伸阅读


      永远让比较函数对相等的值返回false(来自Effective C++)

      C++中的 sort 使用自定义比较函数的具体运行过程是怎样的呢?

      C++自定义比较:仿函数、函数与重载运算符 

    ————全心全意投入,拒绝画地为牢
  • 相关阅读:
    yjh_study_command
    installed_oracle_can't_use
    Database 2 Day DBA guide_Chapter3
    oracle_great_integration_译文
    oracle_set_autocommit
    Database 2 Day DBA guide_Chapter2
    linux_base_commond_two
    linux_base_commond_one
    Oracle_11gR2_概念_第06章_数据字典和动态性能视图_英文词汇
    angular 自定义select选项,tab切换!!!
  • 原文地址:https://www.cnblogs.com/Bw98blogs/p/8371152.html
Copyright © 2011-2022 走看看