c++优先级队列priority_queue compare成员参数分析

c++手册

定义

定义:priority_queue<Type, Container, Functional>在这里插入图片描述

分析

priority_queue <int,vector<int>,less<int> >q;
默认使用容器vector,使用less< T >进行比较,默认为大顶堆。
Q:默认less比较,重载了operator<,应该是升序,为什么是大顶堆?
A:这个问题需要从源码角度分析:
堆push时调用的代码:

__push_heap(_RandomAccessIterator __first, _Distance __holeIndex,
            _Distance __topIndex, _Tp __value, _Compare __comp)
    //__holeIndex  新添加节点的索引,即叫做孔洞
    //__topIndex  顶端索引
    //__value   新添加节点的值
    //__comp    比较函数,传入为less
{
  _Distance __parent = (__holeIndex - 1) / 2;   //找到新节点父节点索引
  while (__holeIndex > __topIndex && __comp(*(__first + __parent), __value)) {
      //若孔洞没有到达最顶端  &&  父节点的值小于新添加节点的值,则要进行下列操作
      //less中,左边比右边小则返回true,与less愿意相同
    *(__first + __holeIndex) = *(__first + __parent);   //将父节点的值放入孔洞
    __holeIndex = __parent; //孔洞的索引编程了原来父节点的索引,往上移动了
    __parent = (__holeIndex - 1) / 2;   //那么孔洞的父节点又要继续往上找
  }
  *(__first + __holeIndex) = __value; //将新添加节点的值放在找到的位置(孔洞)
}

传入的comp就是为了在比较新加入的值value与父节点值的大小,若返回true,才会进行交换操作。
如果传入less< int>,则在父节点比value的小时,comp为true,value上升,父节点下移,对应了大根堆的上溯。greater< int>同理。

在这里插入图片描述

//std::less::operator() 
constexpr bool operator()(const T &lhs, const T &rhs) const 
{
    return lhs < rhs;
}

我们可以按自己需求,修改compare类
【TIP】compare 形参如果是引用,必须使用常量引用
在这里插入图片描述

cmp写法

函数对象

struct CmpByValueLess {//如果类定义了调用运算符,则该类的对象称作函数对象
    bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) {
        return lhs.second < rhs.second;//大顶堆,若>则为小顶堆
    }
};
vector<int> topKFrequent(vector<int>& nums, int k) {
    priority_queue<pair<int, int>, vector<pair<int, int>>, CmpByValueLess> queue;
    //...
}

函数指针

使用普通函数指针或静态成员函数指针。

class Solution {
public:  
		//静态成员函数才相当于外部普通函数
        static bool cmp(pair<int, int>& a, pair<int, int>& b)
        { 
            return b.second<a.second; 
        }
        vector<int> topKFrequent(vector<int>& nums, int k) {
        priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(cmp)*> freq(cmp);
		//...
    }
};

lambda函数

lambda函数可以直接引用捕获,使用函数内其他变量。

vector<int> kthSmallestPrimeFraction(vector<int>& A, int K) {
    using PII=pair<int,int>;
    auto cmp=[&](const PII& a ,const PII& b){
        return A[a.first]*1.0/A[a.second]>A[b.first]*1.0/A[b.second];
    };
    priority_queue<PII,vector<PII>,decltype(cmp)> smallHeap(cmp);
    //...
}

重载operator< 或 >

重载大于小于号
大根堆使用less,重载operator<;
小根堆使用greater,重载operator>。

struct point {
    int val, x, y;
    point(int val, int x, int y) : val(val), x(x), y(y) {}
    bool operator> (const point& a) const { 
    //后面的const表示函数为常量函数,可以对常量对象使用,如果没有const,则常量point无法重载>
        return val > a.val; 
    }
    //另一种写法
    // friend bool operator> (const point& a,const point& b)  { 
    //     return a.val > b.val; 
    // }
};
int main() {
    priority_queue<point,vector<point>,greater<point>> pq;
    pq.push(point(1,2,3));
    getchar();
    return 0;
}

【TIP】用priority_queue一个一个插入元素创建堆时间复杂度为O(nlgn),可以直接用priority_queue<int> b(a.begin(),a.end());
该构造函数调用make_heap(),时间复杂度为O(n)。

<think>好的,我现在要介绍C++中的priority_queue。首先,我需要回忆一下自己对这个容器的理解。priority_queue是STL中的一个容器适配器,它提供了一种优先队列的数据结构,通常用堆来实现。默认情况下,它是一个最大堆,也就是队首元素总是最大的。用户也可以自定义比较方式,比如改成最小堆。 接下来,我应该先说明priority_queue的基本概念。它属于容器适配器,意味着它基于其他容器(比如vector或deque)实现。默认的底层容器是vector,这一点需要提到。然后,它的模板参数有三个:元素类型、底层容器类型和比较函数对象。比如,priority_queue<int, vector<int>, greater<int>>这样的声明会创建一个最小堆。 然后,我需要介绍它的常用操作。比如push()用于插入元素,pop()删除顶部元素,top()访问顶部元素,empty()判断是否为空,size()返回元素数量。需要注意的是,和普通队列不同,priority_queue没有front()或back()方法,只能用top()来获取顶部元素。 接下来,可能应该举几个例子。例如,默认的最大堆用法,如何插入元素并依次弹出最大值。再比如,如何通过自定义比较函数或使用greater<int>来实现最小堆。这里要注意语法是否正确,比如声明时的参数顺序是否正确,是否需要包含相应的头文件,比如<queue>和<functional>。 另外,可能需要提到自定义比较函数的情况。比如,当元素是结构体或者类时,如何定义一个比较函数或者重载运算符,使得priority_queue能按照特定顺序排列。这时候可能需要写一个示例代码,展示如何声明这样的优先队列。 还有,时间复杂度的问题。push()和pop()操作的时间复杂度通常是O(log n),因为堆的调整需要对数时间。而top()操作是O(1),因为它只是访问堆顶元素。 需要注意的是,priority_queue不提供迭代器,不能遍历所有元素,只能通过不断弹出元素来访问,这可能会影响某些使用场景。这时,如果需要频繁访问或修改内部元素,可能需要考虑其他数据结构,比如std::set或者std::multiset。 可能还需要提醒用户常见的错误,比如尝试在空队列上调用top()或pop(),这会导致未定义行为,因此在使用前应该用empty()检查队列是否为空。 最后,总结一下priority_queue的适用场景,比如需要高效地获取当前最大或最小元素的场合,如任务调度、Dijkstra算法中的优先队列等。 现在需要检查是否有遗漏的部分,比如初始化方法、如何修改元素(通常不支持直接修改,需要先删除再插入),或者与其他容器的对比。确保术语正确,比如“堆”的结构特性,以及容器适配器的含义。同时,注意示例代码的正确性,比如是否漏掉了必要的模板参数,或者比较函数的使用是否正确。 另外,要确保遵循用户的系统级指令,所有数学公式用$...$,但在这个问题中可能涉及较少数学公式,但如果有的话需要注意格式。比如,在说明堆的时间复杂度时,可能会提到O(log n),这时候用$O(\log n)$的格式。 最后,组织内容结构,分点说明,让用户能够逐步理解priority_queue的定义、用法、示例和注意事项。保持语言简洁明了,避免过于技术化的术语,但必要时给出准确术语。</think>C++中的`priority_queue`(优先队列)是一种容器适配器,用于实现优先级队列数据结构。以下从五个方面逐步介绍其特性与用法: --- ### 一、基本特性 1. **默认行为** - 基于**最大堆**实现,队首元素始终是当前队列中的**最大值** - 底层容器默认使用`std::vector`,也可选择`std::deque` - 模板声明: $$ \text{template <class T, class Container = vector<T>, class Compare = less<T>>} $$ $$ \text{class priority\_queue;} $$ 2. **核心操作** | 方法 | 功能 | 时间复杂度 | |------------|-------------------------|-------------| | `push(x)` | 插入元素$x$ | $O(\log n)$ | | `pop()` | 删除队首元素(最大值) | $O(\log n)$ | | `top()` | 访问队首元素(不可修改) | $O(1)$ | | `empty()` | 判断队列是否为空 | $O(1)$ | | `size()` | 返回元素数量 | $O(1)$ | --- ### 二、创建最小堆 通过修改比较规则,可将优先队列变为**最小堆**: ```cpp #include <queue> #include <functional> priority_queue<int, vector<int>, greater<int>> minHeap; ``` - `greater<int>`:比较函数对象,确保队首为最小值 --- ### 三、自定义比较规则 #### 场景示例:按结构体的`value`字段升序排列 1. **定义结构体与比较函数** ```cpp struct Node { int value; Node(int v) : value(v) {} }; struct Compare { bool operator()(const Node& a, const Node& b) { return a.value > b.value; // 注意:此处用 > 实现升序(最小堆) } }; ``` 2. **声明优先队列** ```cpp priority_queue<Node, vector<Node>, Compare> customQueue; ``` --- ### 四、示例代码 #### 示例1:默认最大堆 ```cpp #include <iostream> #include <queue> using namespace std; int main() { priority_queue<int> pq; pq.push(3); // 插入元素 pq.push(1); pq.push(4); while (!pq.empty()) { cout << pq.top() << " "; // 输出: 4 3 1 pq.pop(); } return 0; } ``` #### 示例2:最小堆 ```cpp priority_queue<int, vector<int>, greater<int>> pq; pq.push(3); pq.push(1); pq.push(4); // 输出: 1 3 4 ``` --- ### 五、注意事项 1. **安全访问** - 调用`top()`或`pop()`前需检查队列是否为空,否则导致未定义行为 ```cpp if (!pq.empty()) { int val = pq.top(); pq.pop(); } ``` 2. **不支持遍历与修改** - 没有迭代器,无法直接访问中间元素 - 修改元素需先删除再插入(时间复杂度升至$O(n \log n)$) 3. **适用场景** - 需高效获取/删除当前极值的场景(如任务调度、图算法中的Dijkstra算法) --- ### 总结 `priority_queue`通过堆结构优化了极值访问效率,适合处理动态数据中的优先级问题。使用时需注意比较规则的配置及操作的安全性。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值