c++优先级队列priority_queue使用lambda表达式出错问题
在c++17下使用lambda可能遇到以下错误提示信息:error: a lambda expression cannot appear in this context。测试代码如下,使用std::priority_queue创建了一个自定义的优先级队列:#include <iostream>#include <queue>int main() {std::cout <
优先级队列简介
优先级队列priority_queue,可以在队列中自定义数据的优先级, 让优先级高的排在队列前面优先出队。它具有队列的所有特性,包括队列的基本操作,只是在这基础上添加了内部的一个排序,它本质是一个堆实现的。
优先级队列的内部是大小顶堆实现的,弹出pop()
和队首top()
都是获得堆首(根结点)的元素。
std::less<T>
变成大顶堆(从上层到下层,堆元素是从大到小,同层之间随便)std::greater<T>
变成小顶堆(从上层到下层,堆元素是从小到大,同层之间随便)
基本操作有:
empty( ) //判断一个队列是否为空
pop( ) //删除队顶元素
push( ) //加入一个元素
size( ) //返回优先队列中拥有的元素个数
top( ) //返回优先队列的队顶元素
优先队列的时间复杂度为O(logn),n为队列中元素的个数,其存取都需要时间。
⼆叉堆是⼀种特殊的⼆叉树(完全⼆叉树),因为堆是基于完全二叉树,所以我们不需要用链式结构来表示,我们可以直接用数组存。
假设父节点的下表为parent,从父节点获取子节点:
左节点下标: 2*parent+1
右节点下标: 2*parent+2
假设子节点的下标为son(左右子节点都可以):
父节点下标:(son-1)/2
小顶堆图示:
小顶堆的建立和删除都是 下沉 操作,添加做的是 上浮 操作。
大顶堆图示:
如上图所示,假设父节点的下标 parent=0,则其左子节点下标为Lchildren=2*parent+1。
右子节点下标为Rchildren=2*parent+2,如上示为例:
第 0 个数据的下标:parent = 0
第 1 个数据的下标:Lchildren = 2*parent + 1 = 2*0+1 = 1
第 2 个数据的下标:Rchildren = 2*parent + 2 = 2*0+2 = 2
⼀般的链表⼆叉树,我们操作节点的指针,⽽在数组⾥我们把数组索引作为指针。
问题描述
在c++17下,priority_queue优先级队列使用lambda表达式,可能遇到以下错误提示信息:
error: a lambda expression cannot appear in this context。
测试创建了一个自定义的优先级队列,测试代码如下:
#include <iostream>
#include <queue>
int main() {
std::cout << "hello test" << std::endl;
using Ty = std::pair<std::string,int>;
std::priority_queue<Ty,
std::vector<Ty>,
decltype( [](Ty a, Ty b)->bool{
return a.second > b.second;
})> q;
q.emplace(std::make_pair("yang",3));
q.emplace(std::make_pair("yong",2));
q.emplace(std::make_pair("zhen",1));
std::cout << "q.top()=" <<q.top().first <<std::endl;
return 0;
}
这段代码在c++20下面测试是ok的。附测试网站地址:GDB online Debugger | Compiler - Code, Compile, Run, Debug online C, C++
换成c++17下测试:
原因分析
Your code is valid C++20 as written but invalid C++17 or earlyer.
可能你使用了c++20的特性,在c++20之前不支持。
在 C++20 之前闭包类型不是默认可构造的。在 C++20 中没有捕获的闭包类型是默认可构造的。
参考这个回答:
C++: lambda-expression in unevaluated context
1.Lambda expressions are not allowed in unevaluated contexts (such as decltype) before C++20.
2.Closure types are not default constructible before C++20. In C++20 a closure type that has no capture is default constructible.
解决之道
问题原因清楚了,如何解决?不能轻易的就换成c++20的工具链吧。方法还是有的,可以改为仿函数实现。定义一个myGreater仿函数解决:
#include <iostream>
#include <queue>
using Ty = std::pair<std::string,int>;
struct myGreater {
bool operator() (Ty a, Ty b){
return a.second > b.second; //大顶堆
}
};
int main() {
std::cout << "hello test" << std::endl;
std::priority_queue<Ty,
std::vector<Ty>,
myGreater> q;
q.emplace(std::make_pair("yang",3));
q.emplace(std::make_pair("yong",2));
q.emplace(std::make_pair("zhen",1));
std::cout << "q.top()=" <<q.top().first <<std::endl;
return 0;
}
如果使用std::greater这个默认的比较器会怎样?它先比较pair的第一个元素。如上例假若改为
std::greater<Ty>,输出结果为:"yang",若第一个元素相同则比较第二个元素。不过既然是pair就该使用自定义的比较器。
priority_queue()默认按照从小到大排列,less,最大堆(降序)。所以top()返回的是最大值而不是最小值。
使用greater<>后,数据从大到小排列,最小堆(升序),top()返回的就是最小值而不是最大值。
附,优先级队列的简单模拟实现(最大堆):
// 默认最大堆,less,从大到小排序 最大的堆顶最先pop
#include <iostream>
#include <math.h>
const size_t MAX_NUM = 50;
template <typename T>
class priority_queue{
public:
T data[MAX_NUM]; // 存储元素的数组
int count = 0; // 当前队列中的元素个数
public:
bool empty(){
return count == 0;
}
int top(){
if (!empty()) {
return data[0];
}
return -1;
}
void push(T key){
data[count] = key;
swim(count);
count++;
}
void pop(){
count--;
exch(0, count);
data[count] = -1;
sink(0);
}
/* 上浮第k个元素 */
void swim(int k){
while ( (k > 0) && less(parent(k), k)) {
exch(parent(k), k);
k = parent(k);
}
}
/* 下沉第k个元素 */
void sink(int k){
while (left(k) < count) {
int older = left(k);
if (right(k) < count && less(older, right(k))) {
older = right(k);
}
if (less(older, k)) break;
exch(k, older);
k = older;
}
}
int parent(int child){
return (child-1)/2;
}
int left(int root){
return root*2 + 1;
}
int right(int root){
return root*2 + 2;
}
/* data[i] 是否⽐ data[j] ⼩? */
bool less(T i, T j){
return data[i] < data[j];
}
/* 交换数组的两个元素 */
void exch(int i, int j){
T temp = data[i];
data[i] = data[j];
data[j] = temp;
}
void print(){
for(int i = 0 ; i < count; i++){
std::cout << " " << data[i] << std::endl;
}
}
};
int main(){
std::cout << "hello test" << std::endl;
priority_queue<int> q;
q.push(12);
q.push(11);
q.push(7);
q.push(8);
q.push(9);
q.push(3);
q.push(2);
q.push(5);
q.push(6);
q.print();
std::cout << "q.top:" << q.top() << std::endl;
q.pop();
q.print();
std::cout << "test over" << std::endl;
}
引用
c++ 优先队列(priority_queue)_STATICHIT静砸的博客-CSDN博客_c++ 优先队列
什么是二叉堆?_韩师学子--小倪的博客-CSDN博客_二叉堆
更多推荐
所有评论(0)