130人参与 • 2024-09-02 • C/C++
priority_queue是c++标准库中的一个容器适配器,用于实现优先队列(priority queue)的数据结构。优先队列是一种特殊的队列,其中的元素按照一定的优先级进行排序,每次取出的元素都是优先级最高的。它的优先级是可以通过传入参数自己调整的,它的底层实现通常使用堆(heap)数据结构。以下是堆结构的学习链接:
主要特点

模板参数
priority_queue的模板定义通常包含三个参数:

以上是priority_queue的接口函数,该篇文章只来学习和模拟c++11以前的接口。
以下是这些接口的使用:
//使用示例
#include<iostream>
#include<queue>
using namespace std;
//这是自定义优先级的一种格式
template<typename t>
class gt
{
public:
//如果返回true则b优先,返回false则a优先,所以这里使用gt会生成小堆
bool operator()(t a, t b)
{
return a > b;
}
private:
};
int main()
{
priority_queue<int> heap1;//int表示储存的类型
priority_queue<int, vector<int>> heap2;//这里vector表示使用的底层容器,这里也可以换成deque<int>。
//greater为编译器提供的类模板,默认的优先级是大堆,而使用它可以生成小堆。
priority_queue<int, vector<int>, greater<int>> heap3;
//当然也可以自己设计一个优先级方式传入,该方法常常用于储存自定义类型,而内置类型编译器提供的就够用。
priority_queue<int, vector<int>, gt<int>> heap4;
//push接口用于存入元素
heap4.push(2);
heap4.push(7);
heap4.push(1);
heap4.push(5);
cout << heap4.size() << endl;//size用于计算队列中元素的个数
while (!heap4.empty())//empty用于判断队列是否为空
{
cout << heap4.top() << ' ';//top获取队头元素
heap4.pop();//pop删除队头元素
}
return 0;
}以上输出为:

首先为了区别于库里面的优先级队列,我们可以用命名空间限制它的作用域。通过观察库里面的priority_queue模板参数一共有三个,第一个为储存的元素类型,第二个参数为需要用的底层容器(默认为vector),第三个参数为用来调整优先级的类模板(需要我们写一个默认模板),那么我们可以做以下设计:
#include<iostream>
#include<vector>
using namespace std;
namespace byte//用命名空间限制它的作用域,来区别于库里面的优先级队列
{
template<typename t, class container = vector<t>, class compare = less<t>>
class priority_queue
{
public:
//...
private:
//...
};
}注意:这里vector模板我们可以使用库里面的,但优先级队列模板(less<t>)需要我们自己写。
stl的任何模拟,如果在考虑成员变量如何设计而发愁,我们可以去想如何储存对象的数据进而来设计我们的成员变量。这里我们用的是容器container(即vector<int>)来储存数据的,所以我们的成员变量之一类型一定是container,其次因为想到不同对象传入的compare可能不同,具有特殊性,所以我们也可以把compare(优先级调整的类模板)作为成员变量。如下:
private: container arr; compare comp;
因为在priority_queue模拟中并不涉及到动态内存开辟和一些特殊理,所以这里的构造函数用编译器默认提供的构造函数就够用了,并不需要我们自己编写。
在优先级队列中因为涉及到堆的调整所以在push和pop接口的设计中有些复杂,其他接口的设计都非常简单就不在讲解,后面大家直接看源码。
首先需要把arr看做是一个堆结构,无论arr里面有没有元素,然后直接把元素push_back到arr中,此时该元素所在位置为堆底,而且并不一定是正确的位置,接下来要做的就是对该元素进行向上调整。
父子节点的定义:子节点记为child,父节点记为father。
child=arr.size()-1
father=(child-1)/2(理解原理后可以当做公式记忆,可通过一下链接参考学习)
向下调整的方法我在以下文章中有具体讲解:
该函数主要功能是pop堆顶元素,但是如果直接pop堆顶元素(下标为0)的话,那么下标为1的会成为堆顶元素,会使原来的父子关系和兄弟关系混乱,要重新调整起来极其复杂,所一需要换一种方案,我们可以把堆顶元素与堆底元素交换然后pop堆底元素,然后对堆进行向下调整。
父子节点的定义:子节点记为child,父节点记为father。
father=0
child=father*2+1(这里father和child的计算公式是可以互推的)
向下调整的方法我在以下文章中有具体讲解:
#include<iostream>
#include<vector>
using namespace std;
namespace byte
{
template<typename t>
class less
{
public:
//如果返回true则b优先,返回false则a优先,所以这里使用gt会生成小堆
bool operator()(t a, t b)
{
return a < b;
}
private:
};
template<typename t>
class greater
{
public:
//如果返回true则b优先,返回false则a优先,所以这里使用gt会生成小堆
bool operator()(t a, t b)
{
return a > b;
}
private:
};
template<typename t, class container = vector<t>, class compare = less<t>>
class priority_queue
{
public:
void adjustup()
{
int child = arr.size() - 1, father = (child - 1) / 2;
while (child > 0)
{
if (comp(arr[father], arr[child]))
{
std::swap(arr[child], arr[father]);
child = father;
father = (child - 1) / 2;
}
else
{
break;
}
}
}
void adjustdown()
{
int father = 0, child = father * 2 + 1;
while (child < arr.size())
{
if (child + 1 < arr.size() && comp(arr[child], arr[child + 1]))
{
child++;
}
if (comp(arr[father], arr[child]))
{
std::swap(arr[child], arr[father]);
father = child;
child = father * 2 + 1;
}
else
{
break;
}
}
}
void push(t x)
{
arr.push_back(x);
adjustup();
}
void pop()
{
std::swap(arr[0], arr[arr.size() - 1]);
arr.pop_back();
adjustdown();
}
t top()
{
return arr[0];
}
size_t size()
{
return arr.size();
}
bool empty()
{
return arr.size() == 0;
}
private:
container arr;
compare comp;
};
}到此这篇关于c++中priority_queue模拟的实现的文章就介绍到这了,更多相关c++ priority_queue模拟内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论