【C++】优先级队列介绍与模拟实现

💞💞 前言

hello hello~ ,这里是大耳朵土土垚~💖💖 ,欢迎大家点赞🥳🥳关注💥💥收藏🌹🌹🌹
在这里插入图片描述

💥个人主页:大耳朵土土垚的博客
💥 所属专栏:C++入门至进阶
这里将会不定期更新有关C++的内容,希望大家多多点赞关注收藏💖💖

目录

  • 💞💞 前言
  • 1.什么是优先级队列
  • 2.仿函数的介绍
  • 3.优先级队列使用
  • 4.优先级队列模拟实现
    • ✨堆向下调整算法
    • ✨堆向上调整算法
    • ✨仿函数
    • ✨优先级队列模拟实现
    • ✨测试代码
  • 5.结语

1.什么是优先级队列

优先级队列是一种特殊的队列,其中的元素都被赋予了优先级。元素的优先级决定了它们在队列中的顺序。在优先级队列中,元素按照优先级从高到低的顺序出队列。

优先级队列可以通过不同的数据结构来实现,常用的有二叉堆、二叉搜索树和斐波那契堆等。

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

2.仿函数的介绍

仿函数(Functor)是一种重载了函数调用运算符()的类或结构体,它可以像函数一样被调用。通过重载函数调用运算符,仿函数可以实现自定义的操作行为。

仿函数可以像普通函数一样接受参数,并返回结果。它可以用于函数对象的传递、函数指针的替代、算法的灵活性增加等场景。

使用仿函数的步骤如下:

  1. 定义一个仿函数类或结构体,重载函数调用运算符()。可以根据需要定义构造函数、析构函数和其他成员函数。
  2. 在代码中创建仿函数对象。仿函数对象可以像函数一样调用,传入参数,并返回结果。

下面是一个示例,演示了使用仿函数实现求平方的功能:

// 定义仿函数类
struct Square {
    int operator()(int x) const {
        return x * x;
    }
};

int main() {
    Square square;  // 创建仿函数对象
    int result = square(5);  // 调用仿函数
    // result = 25
    return 0;
}

在上述示例中,Square 是一个仿函数类,重载了函数调用运算符()。通过创建 Square 对象 square,并像函数一样调用
square(5),可以得到5的平方值25。

通过仿函数,我们可以实现更灵活和自定义的操作行为,并且可以与STL算法等标准库函数配合使用,提高代码的可读性和可维护性。

3.优先级队列使用

函数声明接口说明
priority_queue()/priority_queue(first,last)构造一个优先级队列
empty( )检测优先级队列是否为空,是返回true,否则返回false
top( )返回优先级队列中最大(最小元素),即堆顶元素
push(x)在优先级队列中插入元素x
pop()删除优先级队列中最大(最小)元素,即堆顶元素

测试代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<vector>
#include <queue>
#include <functional> // greater算法的头文件
void TestPriorityQueue()
{
	// 默认情况下,创建的是大堆
	vector<int> v = { 3,2,7,6,0,4,1,9,8,5 };
	priority_queue<int> q1;

	//使用范围for入队列
	for (auto& e : v)
		q1.push(e);

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


	// 如果要创建小堆,将第三个模板参数换成greater比较方式
	priority_queue<int, vector<int>, greater<int>> q2(v.begin(), v.end());
	while (!q2.empty())
	{
		cout << q2.top() << " ";
		q2.pop();
	}
}

int main()
{
	TestPriorityQueue();
	return 0;
}

结果如下:
在这里插入图片描述

因为实现大堆还是小堆的底层逻辑是不一样的,对应得代码也有些许差异,但为了减少代码的量,提高程序员编程的效率,我们就可以在上述优先级队列的类模板中再传入一个仿函数,对于不同的堆传不同的仿函数类以实现不同的需求;

模板不能直接传入函数,但是可以传类型,可以是自定义类型也可以是内置类型,所以可以传入一个仿函数(它本质是一个类)

4.优先级队列模拟实现

优先级队列模拟实现和队列类似,所不同的是每次插入数据后都会使用算法将队列中的数据调整为一个堆,每次删除也是删除堆顶元素,然后将剩余的元素再次调整为一个堆,这样每次堆顶的元素就是所有数据中优先级最高的那一个了,对于堆算法有疑问的可以点击数据结构——堆的介绍与实现查看

✨堆向下调整算法

现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。
向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

int array[] = {27,15,19,18,28,34,65,49,25,37};

在这里插入图片描述

🥳🥳 下面介绍向下调整为小堆
前提条件——左右子树都是小堆

//堆向下调整算法(小堆)
void AdjustDown(HPDataType* a, int n,int parent)
{
	
	int child = parent * 2 + 1;
	
	//向下调整
	while (parent < n)
	{
	//找到较小的孩子节点
		if (child + 1 < n && a[child] > a[child + 1])
		{
			child++;
		}
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = child * 2 + 1;
		}
		else
			break;
		
	}
}

因为要调整为小堆,所以要找到孩子中较小的一个进行比较;
如果父节点小于较小的孩子节点则直接break不需要调整,因为向下调整的前提条件是——左右子树都是小堆
调整前:
在这里插入图片描述
调整后:在这里插入图片描述

所以我们就可以利用堆向下调整算法来将堆顶元素与最后一个元素交换后,删除交换后最后一个元素,保证除了交换后堆顶元素外,左右子树都是一个堆,然后利用堆向下调整算法将整个二叉树调整为一个堆

此外堆向下调整算法还可以将一串数据调整为一个堆:
在这里插入图片描述

当然堆向上调整算法也可以实现,只不过它的时间复杂度没有堆向下调整算法好,所以我们选择使用堆向下调整算法构建堆

✨堆向上调整算法

我们知道堆的父节点必须都大于或小于子节点,那么往一个堆中插入元素是没办法保证大于或小于其父节点的,所以我们插入之后需要调整这个二叉树来保证堆;
这里就要用到堆向上调整算法了;注意下面是小堆的调整

堆向上调整算法

//向上调整
void AdjustUp(HPDataType* a,int child)
{
	//找到双亲节点
	int parent = (child - 1) / 2;
	//向上调整
	while (child > 0)
	{
		if (a[parent] > a[child])
		{
			Swap(&a[parent], &a[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
			break;
		
	}
}

堆向上调整类似于向下调整也有大堆小堆之分,大家可以依照堆的向下调整自己试试看写一下大堆的向上调整

✨仿函数

有了堆向下调整算法来删除堆顶元素和建堆,以及堆向上调整算法尾插元素,我们就可以实现优先级队列了🥳🥳
但是优先级队列能否按照我们需要选择大堆还是小堆呢?
这就需要利用我们之前学习的仿函数,传入一个类模板class Compare,当我们想要建小堆的时候就选择相应的类即可

//实现大堆
template<class T>
    struct Less 
    {
        bool operator()(const T& x, const T& y)
        {
            return x < y;
        }
    };

//实现小堆
 template<class T>
    struct Greater
    {
        bool operator()(const T& x, const T& y)
        {
            return x > y;
        }
    };

注意这里类的名字是我们自己取的,为了和STL库里面的命名保持一致,我们使用Less建立大堆,Greater建立小堆,因为建大堆还是小堆关键就在于父节点与孩子节点比较是大于还是小于,所以我们将所有比较的地方都使用仿函数,这样就可以按照我们希望的方式来比较,如果希望是大堆,就按Less中的<来比较;小堆就按照Greater中的>来比较

这样就可以将上述仿函数传给优先级队列了:

  //优先级队列的模拟实现
    template<class T, class Container = vector<T>,class Compare = Less<T>>//默认是大堆
    class priority_queue
    {}

✨优先级队列模拟实现

#pragma once
#include<iostream>
using namespace std;
#include<vector>
#include <queue>
#include <functional> // greater算法的头文件
namespace tutu //防止命名冲突
{

    template<class T>
    struct Less 
    {
        bool operator()(const T& x, const T& y)
        {
            return x < y;
        }
    };

    template<class T>
    struct Greater
    {
        bool operator()(const T& x, const T& y)
        {
            return x > y;
        }
    };

    //优先级队列的模拟实现
    template<class T, class Container = vector<T>,class Compare = Less<T>>
    class priority_queue
    {
    public:
        //强制生成默认构造函数
        priority_queue() = default;
        
        //迭代器构造
        template<class InputIterator>
        priority_queue(InputIterator begin, InputIterator end)
        {
            //入队列
            while (begin != end)
            {
                _c.push_back(*begin);
                begin++;
            }

            //调整数据为堆
            for (int i = ((int)_c.size() - 1-1)/2; i >= 0; i--)
            {
                Adjust_Down(i);
            }
        }

        //initializer_list构造
        priority_queue(initializer_list<T> il)
        {
            for (const auto& e : il)
            {
                _c.push_back(e);
            }

            //调整数据为堆
            for (int i = ((int)_c.size() - 1-1)/2; i >= 0; i--)
            {
                Adjust_Down(i);
            }
        }


        //向上调整算法
        void Adjust_Up(int child)
        {
            int parent = (child - 1) / 2;
            while (child > 0)
            {
                if (_comp(_c[parent] , _c[child]))
                {
                    swap(_c[parent], _c[child]);
                    child = parent;
                    parent = (child - 1) / 2;
                }
                else
                {
                    break;
                }
            }
        }
        
        //插入元素
        void push(const T& val)
        {
            _c.push_back(val);
            Adjust_Up(_c.size() - 1);
        }

        //向下调整算法
        void Adjust_Down(int parent)
        {
            int child = parent * 2 + 1;
            while (parent< _c.size())
            {
                //找更大的孩子节点
                if (child + 1 < _c.size() && _comp(_c[child ] , _c[child+1]))
                {
                    child++;
                }

                if (child<_c.size() && _comp(_c[parent] , _c[child]))
                {
                    swap(_c[parent], _c[child]);
                    parent = child;
                    child = parent * 2 + 1;
                }
                else
                {
                    break;
                }
            }
        }

		//删除堆顶元素
        void pop()
        {
            swap(_c[0], _c[_c.size() - 1]);
            _c.pop_back();
            Adjust_Down(0);
        }
		
		//取堆顶元素
        T& top()
        {
            return _c[0];
        }
		
		
        const T& top()const
        {
            return _c[0];
        }


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

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

    private:
        Container _c;//底层容器
        Compare _comp; //比较方式
    };


   
}

✨测试代码

 //测试代码
    void test1()
    {
        priority_queue<int> p;
        p.push(1);
        p.push(8);
        p.push(3);
        p.push(4);
        p.push(6);
        p.push(2);
       
            while (!p.empty())
            {
                cout << p.top() << " ";
                p.pop();
            }
    }


    //迭代器构造测试
    void test2()
    {      
        vector<int> v{ 1, 7, 8, 4, 5, 9, 2, 3, 6 };
        priority_queue<int> p(v.begin(), v.end());
        while (!p.empty())
        {
            cout << p.top() << " ";
            p.pop();
        }
    }


    //initializer_list构造测试代码
    void test3()
    { 
        priority_queue<int> p = { 1,4,7,5,3,9,2,6,8 };
        while (!p.empty())
        {
            cout << p.top() << " ";
            p.pop();
        }

    }

我们使用test1测试,结果如下:

在这里插入图片描述

发现每次取的都是堆顶的元素(也就是数据中优先级最高的那一个)

5.结语

前面我们学习过栈和队列,优先级队列和它们类似,所不同的是每次插入数据都需要使用堆算法来调整建堆,删除堆顶数据后也需要进行建堆,这样每次堆顶元素都是优先级最高的那个元素,以上就是优先级队列的所有内容啦~ 完结撒花 ~🥳🎉🎉

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/680632.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

冒泡函数模拟qsort函数

有关于qsort函数和冒泡函数可以跳转CSDNhttps://mp.csdn.net/mp_blog/creation/editor/139388503 qsort的底层逻辑是这样的 我们冒泡排序模仿qsort必须要针对的是所以说我们要模仿qsort的底层逻辑来写 我们以整型数组来举例 #include <stdio.h> int cmp_int(const vo…

OpenAI的Sam Altman搞核聚变了?!究竟是创新还是疯狂?|TodayAI

据《华尔街日报》报道&#xff0c;西雅图地区的核聚变公司Helion Energy正在与人工智能公司OpenAI洽谈一项重要交易&#xff0c;OpenAI计划“购买大量电力为数据中心提供动力”。这一消息引起了广泛关注。 OpenAI的首席执行官兼联合创始人Sam Altman已向Helion投资了3.75亿美元…

【python】成功解决“TypeError: ‘method’ object is not subscriptable”错误的全面指南

成功解决“TypeError: ‘method’ object is not subscriptable”错误的全面指南 一、引言 在Python编程中&#xff0c;TypeError: method object is not subscriptable错误是一个常见的陷阱&#xff0c;特别是对于初学者来说。这个错误通常意味着你尝试像访问列表、元组、字典…

为何PHP使用率 大幅度下降!需求量几乎为零!

用PHP的人越来越少的主要原因包括&#xff1a;市场竞争加剧、新技术的出现、性能和安全问题、以及开发者社区的变化。市场竞争加剧是其中一个突出的因素。随着Python、Node.js等现代编程语言的崛起&#xff0c;它们提供了更好的性能、更简洁的语法和更丰富的框架&#xff0c;逐…

Android音频API介绍

Android系统提供了四个层面的音频API&#xff1a; Java层MediaRecorder&MediaPlayer系列&#xff1b;Java层AudioTrack&AudioRecorder系列&#xff1b;Jni层opensles&#xff1b;JNI层AAudio&#xff08;Android O引入&#xff09; 下面分别介绍这些API的使用及特点。…

学习Python我能做些什么了?你真的了解了嘛?

工欲善其事&#xff0c;必先利其器。学习不是盲目的&#xff0c;是有目标性的。所以&#xff0c;在学习之前充分了解自己所学技能的前景&#xff0c;学完能做什么&#xff0c;大概地薪资待遇是很有必要的。 Python作为人工智能的重要编程语言&#xff0c;无论发展前景还是就业…

Python接入淘宝API接口采集商品详情页到手价优惠券信息数据:智能化营销的加速器

在电子商务领域&#xff0c;智能化营销正在成为提高效率和竞争力的关键。淘宝API提供了一套完整的解决方案&#xff0c;帮助商家实现智能化营销&#xff0c;从而提升销售业绩和顾客满意度。 库存管理&#xff1a; 淘宝API使商家能够实时监控库存水平&#xff0c;自动补货&#…

如何使用vsCode打开intel D435i深度相机

一、下载并安装相机SDK文件 1.SDK下载地址&#xff1a; Release Intel RealSense™ SDK 2.0 (v2.54.2) IntelRealSense/librealsense GitHub 2.下载后&#xff0c;双击即可安装 3.环境配置 1&#xff09;window的开始菜单&#xff0c;搜索环境变量&#xff0c;选择编辑系…

WebGL开发时尚设计系统

开发一个基于WebGL的时尚设计系统可以为用户提供一个互动、实时的3D体验&#xff0c;允许他们设计和试穿虚拟服装。这个系统可以广泛应用于时尚设计、电子商务、虚拟试衣间等领域。以下是开发此系统的主要步骤和关键技术。北京木奇移动技术有限公司&#xff0c;专业的软件外包开…

手机录屏怎么没有声音?一键排查,轻松解决!

“手机录屏怎么没有声音&#xff1f;今天录制了一个线上的音乐会&#xff0c;结束的时候发现完全没有声音&#xff0c;我检查了手机的设置&#xff0c;录屏功能看起来是正常的&#xff0c;但为什么就是录不到声音呢&#xff1f;希望大家能帮我解答这个疑惑。” 随着智能手机的…

中电金信:从规划到落地,中电金信全程陪伴式服务助力泛金融数字化转型

在当前的全球经济和金融发展格局中&#xff0c;金融行业正经历着一场以数字化为核心的快速转型。中国银行业和保险业已经成功探索出一条数字化转型的路径&#xff0c;并积累了丰富的实践经验。然而&#xff0c;泛金融领域则仍处于数字化转型的初期阶段&#xff0c;其转型能力因…

【golang学习之旅】Go中的cron定时任务

系列文章 【golang学习之旅】报错&#xff1a;a declared but not used 【golang学习之旅】Go 的基本数据类型 【golang学习之旅】深入理解字符串string数据类型 【golang学习之旅】go mod tidy 【golang学习之旅】记录一次 panic case : reflect: reflect.Value.SetInt using…

书生·浦语大模型全链路开源体系-笔记作业2

全部写成了shell脚本&#xff0c;可以一键执行。 笔记&#xff1a; 1. 环境安装(InternStudio开发机) # 1. 创建conda环境 studio-conda -o internlm-base -t demo # 2. 激活conda环境 conda activate demo # 3. 安装额外的依赖 pip install huggingface-hub0.17.3 pip inst…

【深度学习】目标检测,Faster-RCNN算法训练,使用mmdetection训练

文章目录 资料环境数据测试 资料 https://mmdetection.readthedocs.io/zh-cn/latest/user_guides/config.html 环境 Dockerfile ARG PYTORCH"1.9.0" ARG CUDA"11.1" ARG CUDNN"8"FROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}…

图片和PDF展示预览、并支持下载

需求 展示图片和PDF类型&#xff0c;并且点击图片或者PDF可以预览 第一步&#xff1a;遍历所有的图片和PDF列表 <div v-for"(data,index) in parerFont(item.fileInfo)" :key"index" class"data-list-item"><downloadCard :file-inf…

QT 信号和槽 多对一关联示例,多个信号,一个槽函数响应,多个信号源如何绑定一个槽函数

三个顾客 Anderson、Bruce、Castiel 都要订饭&#xff0c;分别对应三个按钮&#xff0c;点击一个按钮&#xff0c;就会弹出给该顾客送饭的消息。注意这个例子只使用一个槽函数&#xff0c;而三个顾客名称是不一样的&#xff0c;弹窗时显示的消息不一样&#xff0c;这需要一些 技…

EV24CXXA EEPROM 选型

如何选择一个靠谱的EEPROM? EV24C128A EV24C256A EV24C512A 是用得最多的

在哪里可以制作微信点餐功能呢

在繁忙的都市生活中&#xff0c;餐饮行业作为与人们日常生活息息相关的行业&#xff0c;其服务质量和便捷性一直备受关注。随着科技的不断发展&#xff0c;微信点餐功能以其便捷、高效的特点&#xff0c;逐渐成为了餐饮行业的新宠。今天&#xff0c;就让我们一起探讨微信点餐的…

基于SSM的“本科生毕业设计选题系统”的设计与实现(源码+数据库+文档)

基于SSM的“本科生毕业设计选题系统”的设计与实现&#xff08;源码数据库文档) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SSM 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 本科生毕业设计选题系统功能结构图 系统首页界面 课题信…

「漏洞复现」用友NC pagesServlet SQL注入漏洞(XVE-2024-13067)

0x01 免责声明 请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;作者不为此承担任何责任。工具来自网络&#xff0c;安全性自测&#xff0c;如有侵权请联系删…