「C++」priority_queue的模拟实现

在这里插入图片描述


📄前言

不知你是否有听说过priority_queue,它是C++的内置类,又名优先级队列。学习其能帮助你解决许多按照优先级排序的问题。如果你还没有学习过优先级队列,那么本文将带领你走向其第一步。

priority_queue

概念

priority_queue 是一种优先级队列的容器,优先级队列是一种自动排序的容器,每次插入元素都会为其安排到一个适当的位置,以让队列保持有序。

  • priority_queue是一种模板类,默认使用vector作为适配器
  • priority_queue的头节点永远都是最大/小的。

priority_queue的主要成员函数:

函数说明
priority_queue()构建一个空的优先队列
priority_queue(first, last)利用迭代器来构造优先队列
empty()判断是否为空
top()在优先级队列插入元素x
pop()删除队列中最大/小的元素

容器适配器

什么是适配器

priority_queue可以使用deque或vector作为其底层实现的适配器。

适配器是一种设计模式,用于将一个接口转换成另一个接口,以便不兼容的类可以协同工作。著名的stl标准库中也使用了适配器模式,如queue、stack、priority_queue等模板类就是如此。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码部分

priority_queue一般使用数据结构 堆 来进行实现,所以一般需要用到堆的时候,都可以考虑使用priority_queue。

// priority是一种模板类,可以支持伪函数
// 这三个参数分别是数据类型,适配器,伪函数(用于数据比较)
template <class T, class Container = std::vector<T>, class compare = std::less<T>>
class priority_queue
{
public:
	// -----成员函数 --------
private:
	Container con;	//适配器
	Compare comp;	//伪函数
};

插入结点

我们尝试可以把堆的空间想像成一颗完全二叉树,尽管它的底层是一个数组,我们仍可以通过公式来转换。

  • 当前位置为 i i i,则左结点为 ( i ∗ 2 ) + 1 (i*2) + 1 (i2)+1 ,右结点为 ( i ∗ 2 ) + 2 (i * 2) + 2 (i2)+2;
  • 当前位置为 i i i,则父结点位置为 ( i − 1 ) / 2 (i - 1) / 2 (i1)/2

堆的每次插入都要与其父结点比较,如果子节点比父结点大/小就和父结点交换位置。

void push(const T& x)
{
	c.push_back(x);	//适配器的接口复用
	adjust_up(size() - 1);
}

//向上调整
void adjust_up(size_t child)
{
	size_t parent = (child - 1) / 2;
	while(child > 0)
	{
		//利用伪函数进行比较
		if(comp(con[parent], con[child))
		{
			std::swap(con[child], con[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else 
			break;
	}
}
//迭代器版本的构造函数
template <class InputIterator>
priority_queue(InputIterator first, InputIterator last)
:con(first, last)
{
    for(int i = (size()-2) / 2; i >= 0; --i)
    {
        adjust_down(i);
    }
}

删除结点

删除结点就等于把头结点给抛出,为了保持堆的正确性,需要重新建堆,也就是需要把最后的结点给回头结点,再依次比较子节点和父结点的大小,直到结点回到正确位置。

在这里插入图片描述

void pop()
{
    std::swap(con[0], con[size()-1]);
    con.pop_back();
    adjust_down(0);
}

//向下调整
void adjust_down(int parent)
{
    int child = parent * 2 + 1;
    while(child < size())
    {
        if(child + 1 < size() && comp(con[child], con[child+1]))
            child++;	//选出左右孩子最大/小的那个
        
        if(comp(con[parent], con[child]))
        {
            std::swap(con[child], con[parent]);
            parent = child;
            child = child * 2 + 1;
        }
        else 
            break;
    }
}

其余函数

剩余的成员函数都是可以用适配器的接口来复用的,所以省去了很多工作。

bool empty() const
{
    return con.empty();
}

size_t size() const
{
    return con.size();
}
//需要为const对象准备一份
const T& top() const 
{
    return con[0];
}

T& top()
{
    return con[0];
}

📓总结

需要完整代码,可以到我的github去取

接口时间复杂度
push() O ( l o g 2 N ) O(log_2N) O(log2N)
pop() O ( l o g 2 N ) O(log_2N) O(log2N)
top() O ( 1 ) O(1) O(1)
empty() O ( 1 ) O(1) O(1)

📜博客主页:主页
📫我的专栏:C++
📱我的github:github

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值