【C++】priority_queue(优先级队列)


描述

1.优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。

2. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。

3. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。

4. 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:
empty():检测容器是否为空
size():返回容器中有效元素个数
front():返回容器中第一个元素的引用
push_back():在容器尾部插入元素
pop_back():删除容器尾部元素

5. 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定priority_queue类实例化指定容器类,则使用vector。

6. 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数make_heap、push_heap和pop_heap来自动完成此操作。

一、常见接口

在这里插入图片描述

二、模拟实现

优先级队列底层用二叉树堆实现的,所以队列实现起来跟堆大致相同,不过引入一个新的概念,仿函数-仿函数的语法几乎和我们普通的函数调用一样,不过作为仿函数的类,都必须重载 operator() 运算符。因为调用仿函数,实际上就是通过类对象调用重载后的 operator() 运算符。使用仿函数帮助我们更方便实现泛型模板。

//一个简单的仿函数
template<class T>
class Less
{
    
    
public:
	//重载 '()' 
	bool operator()(const T& x,const T& y)
	{
    
    
		//内置类型直接比大小,自定义大小会调用他们自己的运算符重载 ‘<’ 比较大小
		return x<y;
	}
}
#include<iostream>
#pragma once
#include<vector>

namespace tzc
{
    
    
	//此处的最后一个模板参数使用仿函数,默认调用库中的less 这样建堆默认是大堆
	template<class T,class Container =vector<T>,class Compare =less<int>>
	class priority_queue
	{
    
    
	private:
		//向下调整
		void AdjustDown(int parent)
		{
    
    	
			Compare com;
			//找出左右孩子大的
			int child = parent * 2 + 1;
			while (child < _con.size())
			{
    
    
				if (child + 1 < _con.size() && com(_con[child],_con[child + 1]))
				{
    
    
					++child;
				}
				if (com(_con[parent],_con[child]))
				{
    
    
					//孩子结点比父节点还大
					swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else break;
			}

		}
		//向上调整
		void AdjustUp(int child)
		{
    
    
			Compare com;
			int parent = (child - 1) / 2;
			while (child > 0)
			{
    
    
				//如果孩子结点比父节点大交换
				if (com(_con[parent],_con[child]))
				{
    
    
					swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
    
    
					break;
				}
			}
		}


	public:
		priority_queue()
		{
    
    

		}
		template<class InputIterator>
		priority_queue(InputIterator first, InputIterator last)
		{
    
    
			while (first != last)
			{
    
    
				_con.push_back(*first);
				++first;
			}

			//建堆
			for (int i =(_con.size() - 1 - 1) / 2; i >= 0; i--)
			{
    
    
				AdjustDown(i);
			}
		}
		void pop()
		{
    
    
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			AdjustDown(0);
		}
		void push(const T& val)
		{
    
    
			_con.push_back(val);
			AdjustUp(_con.size() - 1);
		}
		const T& top()
		{
    
    
			return _con[0];
		}

		bool empty()
		{
    
    
			return _con.empty();
		}

		int size()
		{
    
    
			return _con.size();
		}

	private:
		Container _con;
	};


	void test_priority()
	{
    
    
		priority_queue<int> pq;
		pq.push(3);
		pq.push(5);
		pq.push(1);
		pq.push(4);

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

	}
}

二、常见Oj面试题

题目链接

数组中的第K个最大元素

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

实例1:

输入: [3,2,1,5,6,4], k = 2
输出: 5

实例2:

输入: [3,2,3,1,2,4,5,5,6], k = 4
输出: 4

解:

方法①

class Solution {
    
    
public:
    int findKthLargest(vector<int>& nums, int k) {
    
    
        //默认大堆  建大堆 
        priority_queue<int> pq(nums.begin(), nums.end());
		//将比第k个大的元素pop出队
        for (int i = 0; i < k-1; i++)
        {
    
    
            pq.pop();
        }
        //取队头元素即为第k个大元素
        return pq.top();
    }
};

方法②

class Solution {
    
    
public:
    int findKthLargest(vector<int>& nums, int k) {
    
    
        //建小堆  建一个有k个元素的小堆
        priority_queue<int,vector<int>,greater<int>> pq(nums.begin(),nums.begin()+k);
        
        //堆顶元素即为k个元素中最小元素,将剩余元素入队,如果比队头元素大,则进队,
        //将原先队头元素出队,这样可以保证队中元素永远只有k个,将所以元素以此方法进队后,队头元素即为第k个大的元素
        for(int i=k;i<nums.size();i++)
        {
    
    
            if(nums[i]>pq.top())
            {
    
    
                pq.pop();
                pq.push(nums[i]);
            }
        }
        return pq.top();
    }
};

猜你喜欢

转载自blog.csdn.net/Tianzhenchuan/article/details/131837116