首页 > 其他分享 >优先级队列(priority_queue)

优先级队列(priority_queue)

时间:2024-10-21 22:19:20浏览次数:9  
标签:queue 优先级 parent 孩子 priority childe const 节点 con

 priority_queue简介

   优先级队列的底层结构其实就是堆,就是我们在学习二叉树的时候学习的堆。它是一种逻辑结构,底层还是vector ,我们把它想象成一个堆结构。

        我们在学习堆的时候,知道了它的父亲和左右孩子的关系。

它如果存在孩子,则一定存在这一种关系,leftchilde=2*parent+1;rightchilde=2*parent+2; 这些都分别对应着它们的下标。

        为什么叫做堆呢,我们下面来看一下。

 我们可以看到,左边的是大顶堆,右边的是小顶堆。观察一下它们有什么特点呢。

        我们再前面讲过二叉树的性质,其实堆实际上就是一颗完全二叉树,并且可以发现,大顶堆的双亲节点都比它的左右孩子要大,小顶堆的双亲节点都要比它的左右孩子要小。

        其实它们的物理结构还是一个数组,只是我们把他们的关心想象成为一颗二叉树,因为完全二叉树特有的性质,它的双亲节点的下标和它们的各自的左右孩子下标都有关系。

        堆是具有下列性质的完全二叉树:每个结点的值都大于或等于其左右孩子节点的值,叫大顶堆(左上图),或者每个节点的值都小于或等于其左右孩子节点的值,叫小顶堆(右上图)。

        注意,这个树的根节点一定是最大的,较大的数相对来说要靠上面一点。

下面我们来模拟实现一下priority_queue。

模拟实现之前,我们还需要了解一下向上调整算法,和向下调整算法。

向下调整算法

向下调整算法就是用双亲节点和左右孩子比较,我们这里以建大堆为例,如果双亲节点小于孩子节点,就将双亲节点的值和孩子节点的值交换,然后让父亲做孩子,再去找父亲的父亲,依次比较,直到最后到根节点。

//向下调整函数
void AdjustDown(int* arr, int size, int root)
{

	//父亲就根节点
	int parent = root;
	//根据父亲找到左孩子
	int child = parent * 2 + 1;

	//当孩子小于数组长度时
	while (child < size)
	{
		//如果左孩子小于右孩子,则将孩子默认为右孩子
		if (child + 1 < size && arr[child] < arr[child + 1])
		{
			child += 1;
		}
		//如果孩子比父亲大,则交换值,再将父亲的值给孩子
		if (arr[child] > arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
		}
		
		parent = child;
		child = parent * 2 + 1;
	}
}

上面这个是使用C语言实现的,我们使用C++实现的时候,由于是使用vector实现的,所以说我们就不会那么复杂。

向上调整算法

每当我们要插入一个值的时候,我们都需要将整个数组变成一个堆,这个时候就需要使用到向上调整算法,因为每一个数都在数组的尾部,所以说我们每次都从插入的那一个数开始进行向上调整,因为我们再插入之前,整个vector就已经是堆了,所以我们只需要将最后一个值与它的祖先一一比较就行了。

	void adjustup()
	{
		//默认孩子是最后一个数
		int childe = con.size() - 1;
		//通过孩子与父亲之前的关系,找到父亲节点
		int parent = (childe - 1) / 2;
		while (childe > 0)
		{
			//默认是升序,建小堆
			//如果父亲比孩子小,就交换位置
			if (com(con[parent], con[childe]))
			{
				swap(con[childe], con[parent]);
				//再更新孩子父亲的下标
				childe = parent;
				parent = (childe - 1) / 2;
			}
			//否则就跳出循环
			else
			{
				break;
			}
		}
	}

仿函数

        由于我们在比较的时候,有可能是升序,有可能是降序,但是我们又不可能写两个排序。我们应该根据用户的需要,提供对应的升序和降序。怎么样在同一个函数中达到这样的要求呢。我们就必须要使用仿函数。

        仿函数本质就是一个类。

        它的类成员函数里面,重载了一个 operator() 。它其实就是用来比较大小的。

        我们先来看一看它们长什么样。

//排升序的仿函数
template <class T>
class Less
{
public:
	bool operator()(const T& x, const T& y)
	{
		return  x< y;
	}
};

//排降序的仿函数
template <class T>
class Greater
{
public:
	bool operator()(const T& x, const T& y)
	{
		return  x > y;
	}
};

为什么叫仿函数,因为我们在使用的时候,确实就和函数调用差不多。

模拟实现代码

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

template <class T>
class Greater
{
public:
	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:

	void adjustdown(int parent)
	{
		int childe = 2 * parent + 1;
		while (childe < con.size())
		{
			if (childe + 1 < con.size() && com(con[childe], con[childe + 1]))
			{
				childe++;
			}

			if (com(con[parent], con[childe]))
			{
				swap(con[childe], con[parent]);
			}

			parent = childe;
			childe = 2 * parent + 1;
		}
	}

	void adjustup()
	{
		//默认孩子是最后一个数
		int childe = con.size() - 1;
		//通过孩子与父亲之前的关系,找到父亲节点
		int parent = (childe - 1) / 2;
		while (childe > 0)
		{
			//默认是升序,建小堆
			//如果父亲比孩子小,就交换位置
			if (com(con[parent], con[childe]))
			{
				swap(con[childe], con[parent]);
				//再更新孩子父亲的下标
				childe = parent;
				parent = (childe - 1) / 2;
			}
			//否则就跳出循环
			else
			{
				break;
			}
		}
	}


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

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

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

	void push(const T& x)
	{
		con.push_back(x);
		adjustup();
	}

	void pop()
	{
		assert(!con.empty());
		swap(con[0], con[con.size() - 1]);
		con.pop_back();
		adjustdown(0);

	}
private:
	container con;
	compare com;
};

整个代码还是比较简单的,就是我们需要了解堆这个结构,然后其它的一些接口也是比较简单的,我们同样的是使用了容器适配器模式。

                                                

标签:queue,优先级,parent,孩子,priority,childe,const,节点,con
From: https://blog.csdn.net/2305_81151565/article/details/143134304

相关文章

  • Linux_进程理解、状态与优先级(详细版)
    1.进程的概念课本概念:程序的一个执行实例,正在执行的程序等。内核观点:担当分配系统资源(CPU时间,内存)的实体。其实:进程=内核的相关管理数据结构(task_struct、页表等)+程序的代码和数据task_struct:是描述进程的结构体,是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着......
  • flume传输数据报错“Space for commit to queue couldn‘t be acquired. Sinks are li
        最近在写一个数据量比较大的项目时候,需要使用flume将kafka中的数据传输到HDFS上进行存储,方便后续的数仓搭建,但是flume在传输数据中却报错如下日志org.apache.flume.ChannelFullException:Spaceforcommittoqueuecouldn'tbeacquired.Sinksarelikelynot......
  • 【优先级评估】模糊小波神经网络攻击目标优先级评估【含Matlab源码 7329期】
    ......
  • 故障5:2022年1月1日所有邮件不能发送接收(Email Stuck in Exchange On-premises Transpo
    凯中故障5:2022年1月1日所有邮件不能发送接收(EmailStuckinExchangeOn-premisesTransportQueues)https://techcommunity.microsoft.com/t5/exchange-team-blog/email-stuck-in-exchange-on-premises-transport-queues/ba-p/3049447https://social.microsoft.com/Forums/zh-C......
  • 【Linux】Linux进程状态与进程优先级
    1.前置知识1.1.并行与并发并发:表示CPU在同⼀个时间内执⾏多个任务并⾏:表示多个CPU在同⼀个时间内执⾏各⾃的任务示意图如下:1.2.时间片时间⽚(timeslice),⼜称为“量⼦(quantum)”或“处理器⽚(processorslice)”,是分时操作系统分配给每个正在运⾏的进程微观上的⼀段CPU时间(在......
  • Bison遇到冲突的默认行为&用户自定义优先级
    Bison遇到冲突的默认行为&用户自定义优先级在使用Bison进行语法分析时,如果在语法规则中存在冲突,Bison会根据默认的优先级和结合性规则进行决策,选择某个特定的行为来解决冲突。Bison中常见的冲突主要包括两类:移入-规约冲突(shift-reduceconflict)规约-规约冲突(reduce-reduce......
  • 进程状态|进程优先级
    目录一、进程状态1.什么是进程状态2.进程状态都包含什么?3.进程状态的查看4.进程退出(1)进程退出的步骤(2)僵尸进程(3)孤儿进程二、进程优先级1.进程优先级是什么?2.为什么要有进程优先级?3.查看进程优先级4.进程优先级的修改(1)为什么nice值范围只有[-20,19]?(2)进程优先级由......
  • C++——stack和queue
    1.简介栈和队列的定义和之前的容器有所差别2.简单地使用voidtest_stack1(){ stack<int>st; st.push(1); st.push(2); st.push(3); st.push(4); while(!st.empty()) { cout<<st.top()<<""; st.pop(); } cout<<endl;}voidtest_queu......
  • C#使用线程安全队列ConcurrentQueue处理数据
    usingSystem;usingSystem.Collections.Concurrent;usingSystem.Collections.Generic;usingSystem.Globalization;usingSystem.Linq;usingSystem.Text;usingSystem.Threading;usingSystem.Threading.Tasks;namespaceConsoleApp10{internalclassProg......
  • 【C++】priority_queue的介绍和模拟实现
    【C++】priority_queue的介绍和模拟实现一.priority_queue的介绍1.priority_queue的基本介绍2.priority_queue的使用介绍二.priority_queue的模拟实现一.priority_queue的介绍1.priority_queue的基本介绍优先队列是一种容器适配器,根据严格的弱排序标准,它的......