c++模拟实现priority_queue

本文介绍了如何在C++中使用优先级队列,包括其基本概念、标准库中的实现以及当标准库提供的仿函数不满足需求时如何自定义。重点讲解了仿函数的概念,以及如何通过添加Compare模板参数来实现升序或降序的大堆和小堆。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

注:写本博客的目的接在记录学习过程,学习c++的新知识点,以及巩固以前学过的知识!!!

/*

1.优先级队列的定义

2.仿函数

3.库中的仿函数不满足时,我们可以自己实现仿函数

*/

priority_queue:优先级队列。

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

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

所以可以把priority_queue当作堆来实现以及使用

结构:因为它为容器适配器,底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭 代器访问,并支持以下操作:

empty():检测容器是否为空

size():返回容器中有效元素个数

front():返回容器中第一个元素的引用

push_back():在容器尾部插入元素

pop_back():删除容器尾部元素

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

那么结构清晰明了

template<class T,class Container>
class priority_queue{
public:
    priority_queue(){}
    template<class iterator>
    priority_queue(iterator first,iterator last){
        while(first!=last){
        _con.push_back(*first);
        first++;
        }
        //向下调整--建堆
        for(int parent=(_con._size()-1-1)/2;parent>=0;parent--){
            adjustDown(parent);
        }
    }
    void push(const T&val){
        _con.push_back(val);
        adjustUp(_con.size()-1);
    }
    void pop(){
        swap(_con[_con.size()-1],_con[0]);
        _con.pop_back();
        adjustDown(0);
    }
    const T& top() const{
	    return _con.front();
	}

	const T& top() const{
		return _con.front();
	}

	size_t size() const{
		return _con.szie();
	}
	bool empty() {
		return _con.empty();
	}
protected:
    adjustDown(int parent){
        int child=parent*2+1;
        while(child<_con.size()){

            if(child+1<_con.size()&&_con[child+1]<_con[child]){
                child++;
            }
            if(_con[child]<_con[parent]){
                std::swap(_con[child], _con[parent]);
                parent = child;
                child = parent * 2 + 1;
            }else{
                break;
            }
        
        }
        
    }
//向上调整
    void adjustUp(size_t child) {
	    size_t parent = (child - 1) / 2;
	    while (child>0) {
		    if (_con[child]<_con[parent]) {
			swap(_con[child], _con[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}else {
			break;
	    }
      }
   }


private:
    Container _con;
}

那如果我们要实现一个大堆呢?还需要修改代码,是不是很麻烦,所以这里要引入一个新的特性:

仿函数。

特点:

仿函数是C++中的一种数据类型,也叫函数对象。它就像函数一样,可以接收参数并返回结果。但不同之处在于,仿函数不是一个普通的函数,而是一个重载了“()”运算符的对象,因此可以像函数一样调用。

仿函数与函数的区别为:

  1. 仿函数是一个类,而函数不是。

  2. 仿函数可以有自己的状态,即它们可以有自己的成员变量,而函数不行。

  3. 仿函数可以重载 operator(),从而实现多种不同的行为,而函数只能通过不同的函数名或参数列表来实现类似的多态。

  4. 仿函数更灵活,可以作为参数传递给更多的函数或算法,而函数只能作为参数传递给支持函数指针的函数。

为了能够支持建立大堆与小堆从而进行升序与降序

template<class T>
struct Less{

    bool operator(const T&left,const T&right){
        return left<right;

        }
    };

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

上面的主代码便可以修改为如下代码

在模板参数里面新增了一个Compare 

	template<class T,class Container=std::vector<T>,class Compare=less<T>>
	class priority_queue {
	public:
		priority_queue()
		{
			
		}
		template<class Iterator>
		priority_queue(Iterator first,Iterator last) {
			while (first!=last) {
				_con.push_back(*first);
				first++;
			}
			//构建堆
			for (int i = (_con.size() - 1) / 2; i >= 0;i--) {
				adjustDown(i);
			}
		}

		void push(const T val) {
			_con.push_back(val);
			//向上调整
			adjustUp(_con.size()-1);
		}

		void pop() {
			std::swap(_con[_con.size()-1],_con[0]);
			_con.pop_back();
			adjustDown(0);
		}
		const T& top() const{
			return _con.front();
		}

		size_t size() const{
			return _con.szie();
		}
		bool empty() {
			return _con.empty();
		}
	protected:
		void adjustUp(size_t child) {
			Compare _compare;  //question1
			size_t parent = (child - 1) / 2;
			while (child>0) {
				if (_compare(_con[child] , _con[parent])) {//question2
					std::swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else {
					break;
				}
			}
		}
		void adjustDown(size_t parent) {
			Compare _compare;
			size_t child = parent * 2 + 1;
			while (child<_con.size()) {
				if ((child + 1 < _con.size()) && _compare(_con[child+1],  _con[child])) {
					child++;
				}
				if (_compare(_con[child] , _con[parent])) {
					std::swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else {
					break;
				}
			}
		}

	private:
		Container _con;
};

思考:question1:使用仿函数时,需不需要生成仿函数的对象?

答:还请看qustion2处的调用,如果我们使用匿名函数:Compare()(_con[child],_con[parent])  使用匿名对象,就不用创建compare对象了。

调用:

下面看这个调用代码:

priority_queue<Data*> q2;

q2.push(new Data(2021,10,1));

q2.push(new Data(2022,10,1));

q2.push(new Data(2023,10,1));

cout<<q2.top()<<ednl;

会发现每次的调用结果都不一样原因是我们的比较代码比较的是Data*这个地址,而比较地址又不是我们想要的,即库里面给出的比较函数不符合我们的预期,我们便可以自己实现比较代码

struct PDataLess{
    bool operator(const Data *Left,const Data *right){
           return *left<*right;
     }
}

调用:priority_queue<Data*,vector<Data*>,PDataLess> q2

我们使用自己的仿函数去比较。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蠢 愚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值