C++优先级队列priority_queue模拟实现

1. priority_queue介绍

优先级队列是一种容器适配器,优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。
注意:默认情况下priority_queue是大堆。

2. priority_queue使用

2.1 priority_queue显示定义

priority_queue默认vector作为容器,默认情况下为大堆。

//默认
priority_queue<int> p1;
//显示为大堆
priority_queue<int, vector<int>, less<int>> p2;

若是容器为vector,结构为小堆

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

2.2priority_queue接口使用

成员函数说明
push往优先级队列中尾插数据
pop删除堆顶元素
top获取堆顶元素
empty判断优先级队列是否为空
size获取优先级队列的元素个数

示例:

void test()
{
	priority_queue<int> pq;
	pq.push(1);
	pq.push(3);
	pq.push(4);
	pq.push(5);
	pq.push(2);
	pq.push(6);
	pq.push(7);

	while (!pq.empty())
	{
		cout << pq.top() << " ";
		pq.pop();

	}
	cout << endl;
}

3. 仿函数

在我们模拟实现priority_queue前,还需要了解仿函数。
仿函数(functor),就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了。
示例:

struct Less
{
	bool operator()(const int& val1, const int& val2)
	{
		return val1 < val2;
	}

};

int main()
{
	Less lessfunc;
	cout << lessfunc(1, 2) << endl;
	cout << lessfunc.operator()(1, 2) << endl;//显示调用
	cout << Less()(1, 2) << endl;//匿名对象

	return 0;
}

4. priority_queue模拟实现

priority_queue底层默认为堆,所以我们需要清楚向上调整算法和向下调整算法(这里以大堆例)。

4.1 向上调整算法

堆的向上调整算法思想(大堆):

  1. 目标结点与父结点相比较
  2. 如果目标结点大于父结点,目标结点就与父结点交换位置,后用当前新的位置作为目标结点继续向上调整。如果目标结点小于父结点,则停止向上调整。此时堆为大堆。
    在这里插入图片描述
    例子: 我们往堆中插入数据89
    在这里插入图片描述
    与父结点相比较大于父结点就交换,继续向上调整。
    在这里插入图片描述
    数据89大于88交换。
    在这里插入图片描述
    直到符合条件为止。
    在这里插入图片描述
    代码:
void adjust_up(size_t child)
{
	size_t parent = (child - 1) / 2;
	while (child > 0)
	{
		if (c[child]>c[parent])
		{
			swap(c[child], c[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

4.2 向下调整算法

堆的向下调整算法思想(大堆):

  1. 目标结点与子结点比较
  2. 若是子结点大于目标结点,则与子结点交换位置。用当前新的位置作为目标结点继续向下调整。若是子结点小于目标结点,则停止向下调整,此时堆为大堆。
    示例:
    此时堆的左右子树都为大堆,因此我们可以将父结点向下调整。
    在这里插入图片描述
    判断子节点与当前位置数据的大小,大于则交换位置。
    在这里插入图片描述
    继续判断,向下调整。
    在这里插入图片描述
    直到符合目标结点大于子结点为止,此时堆为大堆。
    在这里插入图片描述
    代码:
void adjust_down(size_t parent)
{
	size_t  child = parent * 2 + 1;
	while (child < c.size())
	{
		//检查左结点是否大于右结点
		if (child + 1 < c.size() && c[child+1]>c[child])
		{
			child++;
		}
		if (c[child]>c[parent])
		{
			swap(c[child], c[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

4.3 实现priority_queue的接口

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

 size_t size() const
 {
     return c.size();
 }

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

 void push(const T& x)
 {
     c.push_back(x);
     adjust_up(c.size()-1);
 }

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

4.4 使用仿函数控制priority_queue默认大小堆

在这里插入图片描述

我们通过查阅文档可以发现,c++通过改变仿函数对priority_queue队列的默认大小堆的控制。
优势: 相比于函数指针,仿函数更加方便简单,更加利于控制(只需要跟换默认的仿函数)。

4.5 priority_queue模拟实现代码

namespace bit
{
    template <class T>
    struct less
    {
        bool operator()(const T& val1,const T& val2)
        {
            return val1 < val2;
        }

    };

    template <class T>
    struct greater
    {
        bool operator()(const T& val1, const T& val2)
        {
            return val1 > val2;
        }

    };

    template <class T, class Container = vector<T>, class Compare = less<T> >
    //这里默认容器是vector 因为要把优先级队列看成一个大堆,堆的物理结构是有序结构。
    class priority_queue
    {
    public:
        void adjust_up(size_t child)
        {
            Compare com;
            size_t parent = (child - 1) / 2;
            while (child > 0)
            {
                if (com(c[parent],c[child]))
                {
                    swap(c[child], c[parent]);
                    child = parent;
                    parent = (child - 1) / 2;
                }
                else
                {
                    break;
                }
            }
        }

        void adjust_down(size_t parent)
        {
            size_t  child = parent * 2 + 1;
            while (child < c.size())
            {
                Compare com;
                if (child + 1< c.size() && com(c[child],c[child+1]))
                {
                    child++;
                }
                if (com(c[parent], c[child]))
                {
                    swap(c[child], c[parent]);
                    parent = child;
                    child = parent * 2 + 1;
                }
                else
                {
                    break;
                }
            }
        }
        bool empty() const
        {
            return c.empty();
        }
        size_t size() const
        {
            return c.size();
        }
        const T& top() 
        {
            return c[0];
        }
        void push(const T& x)
        {
            c.push_back(x);
            adjust_up(c.size()-1);
        }
        void pop()
        {
            swap(c[0], c[size() - 1]);
            c.pop_back();
            adjust_down(0);
        }
    private:
        Container c;
        Compare comp;
    };
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值