首页 > 编程语言 >【C++】queue和priority_queue

【C++】queue和priority_queue

时间:2024-09-16 11:53:09浏览次数:3  
标签:priority return C++ queue child const size

在这里插入图片描述

个人主页~


queue和priority_queue

一、queue的介绍和使用

1、queue的介绍

queue详解

队列是一种容器适配器,专门用在先进先出操作中,从容器一端插入元素,另一端提取元素

队列作为容器适配器实现,就是将特定容器封装成其底层容器类,queue提供一组特定的成员函数来访问其元素,元素从队尾入队列,队头出队列

底层容器至少要支持empty判空、size大小、front队头、back队尾、push_back尾插、pop_front头删操作

vector是没有办法满足以上操作的,但deque和list是可以的

2、queue的使用

函数声明接口说明
queue构造空队列
empty检测队列是否为空
size返回队列中有效数字个数
front返回队头元素的引用
back返回队尾元素的引用
push在队尾将元素入队
pop将队头元素出队列
void test_queue()
{
	std::queue<int> q;
	q.push(1);
	q.push(2);
	q.push(3);
	q.push(4);
	std::cout << q.size() << std::endl;
	std::cout << q.back() << std::endl;
	while (!q.empty())
	{
		std::cout << q.front() << " ";
		q.pop();
	}
}

在这里插入图片描述

3、queue的模拟实现

namespace little_monster
{
	template<class T,class Container = std::deque<T>>
	class queue
	{
	public:
		queue() 
		{}
		void push(const T& x) 
		{
			_c.push_back(x); 
		}
		void pop() 
		{
			_c.pop_front(); 
		}
		T& back() 
		{
			return _c.back(); 
		}
		const T& back()const 
		{
			return _c.back(); 
		}
		T& front() 
		{
			return _c.front(); 
		}
		const T& front() const 
		{
			return _c.front(); 
		}
		size_t size() const 
		{
			return _c.size(); 
		}
		bool empty() const 
		{
			return _c.empty(); 
		}
	private:
		Container _c;
	};
}

在这里插入图片描述
当然queue的第二个模版参数只能为deque和list,vector是不行的,因为pop_front不是vector的成员
在这里插入图片描述

二、priority_queue的介绍和使用

1、priority_queue的介绍

文档介绍

优先队列priority_queue是一种容器适配器,根据严格的弱排序标准,会变为降序队列

类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素

优先队列被实现为容器适配器,提供一组特定的成员函数来访问其元素,元素从特定容器的尾部弹出

底层容器需要支持empty、size、front、push_back、pop_back操作

标准容器vector、deque满足上述要求,但默认一般为vector

需要支持随机访问迭代器,以便始终在内部保持堆结构,容器适配器在需要时自动调整结构

2、priority_queue的使用

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

函数声明接口说明
priority_queue()/priority_queue(first,last)构造一个空的优先级队列
empty判空
top返回堆顶元素
push在堆中插入元素
pop删除堆顶元素
#include <queue>
#include <functional>//里边有greater比较方式
void TestPriorityQueue()
{
	// 默认情况下,创建的是大堆,其底层按照小于号比较
	std::vector<int> v{ 3,2,7,6,0,4,1,9,8,5 };
	std::priority_queue<int> q1;
	for (auto& e : v)
		q1.push(e);
	// 如果要创建小堆,将第三个模板参数换成greater比较方式
	std::priority_queue<int, std::vector<int>, 
	std::greater<int>> q2(v.begin(), v.end());
	
	while(!q1.empty())
	{
		std::cout << q1.top() << " ";
		q1.pop();
	}
	std::cout << std::endl;

	while(!q2.empty())
	{
		std::cout << q2.top() << " ";
		q2.pop();
	}
	std::cout << std::endl;
}

在这里插入图片描述

如果在priority_queue中放自定义类型的数据,用户需要在自定义类型中自己重载符号,就比如说日期类就要重载>、<,按照我们定义的方式进行比较

手感火热做道题
数组中的第K个最大元素

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        // 将数组中的元素先放入优先级队列中
        priority_queue<int> p(nums.begin(), nums.end());
        // 将优先级队列中前k-1个元素删除掉
        for (int i = 0; i < k - 1; ++i) 
        {
            p.pop();
        }
        return p.top();
    }
};

3、priority_queue的模拟实现

优先级队列就是一个封装好的堆

#pragma once
#include <vector>
namespace little_monster
{
	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;
		}
	};

	template<class T, class Container = std::vector<T>, 
						class Compare = less<T>>
	class priority_queue
	{
	public:
		priority_queue()
			:_c()
		{}

		template <class Iterator>
		priority_queue(Iterator first, Iterator last)
			: _c(first, last)
		{
			int count = _c.size();
			int root = ((count - 2) >> 1);
			for (; root >= 0; root--)
			{
				AdjustDown(root);
			}
		}
		void push(const T& x)
		{
			_c.push_back(x);
			AdjustUp(_c.size() - 1);
		}

		void pop()
		{
			if (empty())
				return;
			std::swap(_c.front(), _c.back());
			_c.pop_back();
			AdjustDown(0);
		}

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

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

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

	private:
		void AdjustDown(int parent)
		{
			size_t child = 2 * parent + 1;
			while (child < _c.size())
			{
				if (child + 1 < _c.size() 
				&& Compare()(_c[child], _c[child + 1]))
				{
					++child;
				}
				if (Compare()(_c[parent], _c[child]))
				{
					std::swap(_c[child], _c[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
					return;
			}
		}

		void AdjustUp(int child)
		{
			size_t parent = ((child - 1) >> 1);
			while (child)
			{
				if (Compare()(_c[parent], _c[child]))
				{
					std::swap(_c[parent], _c[child]);
					child = parent;
					parent = ((child - 1) >> 1);
				}
				else
					return;
			}
		}

	private:
		Container _c;
	};
}

在这里插入图片描述

我们自己定义less和greater以控制是大堆还是小堆,封装在一个结构体中,作为priority_queue的第三个模版参数

主要的就是向上调整算法和向下调整算法,与之前C语言学过的一样,稍有改变

三、仿函数

1、仿函数的特征

优先级队列中的less和greater叫做仿函数

重载圆括号运算符:仿函数的核心在于它重载了圆括号"()"运算符,这使得类的实例能够接收参数,并返回一个值

灵活性和状态保存:与普通函数相比,仿函数具有更大的灵活性,因为它可以包含成员变量,这意味着在多次调用仿函数时,它可以保持并更新这些状态信息,从而影响其行为或返回值

2、仿函数的使用

仿函数实际上就是重载括号,使用起来跟函数指针类似,它不仅能够像函数一样被调用,又具有类和对象的特性,像我们之前如果写向上调整算法以及向下调整算法,大堆和小堆是需要到算法中修改代码的,但是有了仿函数就可以直接重载()然后直接调整是less还是greater就好了

ex、有关于list反向迭代器

template<class Iterator, class Ref, class Ptr>
class ReverseIterator
{
public:
	typedef ReverseIterator<Iterator, Ref, Ptr> Self;

	ReverseIterator(Iterator it)
		:_it(it)
	{}

	Self& operator++()
	{
		--_it;
		return *this;
	}

	Self& operator--()
	{
		++_it;
		return *this;
	}

	Ref operator*()
	{
		Iterator cur = _it;
		return *(--cur);
	}

	Ptr operator->()
	{
		return &(operator*());
	}

	bool operator!=(const Self& s)
	{
		return _it != s._it;
	}

	bool operator==(const Self& s)
	{
		return _it == s._it;
	}
private:
	Iterator _it;
};

对正向迭代器进行封装就可以得到反向迭代器,先有正向再有反向


今日分享就到这里了~

在这里插入图片描述

标签:priority,return,C++,queue,child,const,size
From: https://blog.csdn.net/s_little_monster/article/details/141940326

相关文章

  • 用chatgpt让自己练习C++匿名函数
    再也不用去搜罗题目了,chatgpt1分钟搞定!如何使用ChatGPT帮助自己学习1、写prompt帮我写一个prompt,让LLM出一个c++专项训练的题目和题解,比如用户想学习C++的匿名函数,LLM就生成一个小型题目,题解全部是用C++匿名函数实现的。chatgpt:当然可以!以下是一个为训练C++匿名函......
  • C++ 成员函数指针简单测试
    classDog{public:voidUpdate_Func(shorti);short(Dog::*pfunc)(short);std::function<short(short)>ffunc;public:shortgoodMorning(shortid);shortgoodAfternoon(shortid);};voidDog::Update_Func(shorti){switch(i)......
  • 【GESP】C++一级练习BCQM3005,基本输出语句printf
    一道基础练习题,练习基本输出语句printf。BCQM3005题目要求描述输出表达式1234∗5678的结果。输入无输出1234∗5678=7006652输入样例无输出样例1234*5678=7006652全文详见个人独立博客:https://www.coderli.com/gesp-1-bcqm3005/【GESP】C++一级练习B......
  • C++数据结构-二叉树的三种遍历方法(进阶篇)
    1.遍历简介:树作为非线性数据结构,在我们取出数据时就需要设计遍历,所谓遍历,就是按照一定的规则性,将数据结构中的所有数据全部依次访问,而二叉树本身并不具有天然的全局次序,故为实现遍历,需通过在各节点与其孩子之间约定某种局部次序,间接地定义某种全局次序,这便是我们常规定的先序......
  • C++数据结构-二叉树的存储方法(基础篇)
    1.简介根据前文的介绍,我们知道了二叉树的性值,其就是一种每一个结点中只允许拥有左右孩子(或为空)的树,这种数据结构在我们的实际设计中非常常用,如前文提到的STL中的set集合,其底层就是一颗标准的红黑树(二叉树的一种),我们这里以创建一颗二叉树并实现通过特定的插入顺序和读取顺序达......
  • C++ 左值和右值
    一般而言,一个左值表达式表示的是一个对象的身份,而一个右值表达式表示的是对象的值。我们不能将其绑定到要求转换的表达式、字面常量或是返回右值的表达式(参见2.3.1节,第46页)。右值引用有着完全相反的绑定特性:我们可以将一个右值引用绑定到这类表达式上,但不能将一个右值引用......
  • C++-练习-40
    题目:编写一个程序,她每次读取一个单词,知道用户只输入q。然后,该程序指出有多少个单词以元音大头,而多少个单词以辅音大头,还有多少个单词不属于着两类。源代码:#include<iostream>#include<cctype>//元音:A、E、I、O、Uintmain(){ usingnamespacestd; charword[20];......
  • 南沙C++信奥老师解一本通题:2110:【例5.1】素数环
    ​【题目描述】输入正整数n,把整数1,2,…,n 组成一个环,使得相邻两个整数之和均为素数。【输入】输入正整数n。【输出】输出任意一个满足条件的环。【输入样例】6【输出样例】432561【提示】数据满足:4≤n≤30#include<bits/stdc++.h>usingnamespace......
  • 南沙C++信奥老师解一本通题 1228:书架
    ​ 【题目描述】John最近买了一个书架用来存放奶牛养殖书籍,但书架很快被存满了,只剩最顶层有空余。John共有NN头奶牛(1≤N≤20,000),每头奶牛有自己的高度Hi(1≤Hi≤10,000),N头奶牛的总高度为S。书架高度为B(1≤B≤S<2,000,000,007)。为了到达书架顶层,奶牛可以踩着其他奶牛的......
  • C++ 教程 #1
    目录1.IDE下载2.基础框架2.1头文件2.2命名空间2.3定义主函数2.4返回值3.变量与常量变(常)量类型表格3.1定义变量3.2定义常量3.3注意事项4.输入与输出4.1输入输出流4.2格式化输入输出5.作业5.1P1001A+BProblem5.2P5708【深基2.习2】三角形面......