Lesson12(初阶)---queue

Lesson12—queue

本篇博客介绍了c++queue的介绍使用以及模拟实现



前言

queue的文档:https://cplusplus.com/reference/queue/queue/
翻译:

  1. 队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元素,另一端
    提取元素。
  2. 队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从队尾入队列,从队头出队列。
  3. 底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操作:
    在这里插入图片描述
  4. 标准容器类deque和list满足了这些要求。默认情况下,如果没有为queue实例化指定容器类,则使用标准容器deque。
  5. 在这里插入图片描述

提示:以下是本篇文章正文内容,下面案例可供参考

一、queue的成员函数

示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。
常用的就这些,和我上一篇的stack一模一样只不过一个是先进后出一个是先进先出

1 queue

在这里插入图片描述
就构造一个空队列

2.empty

检查这个队列是不是空的,返回值是一个布尔类型

在这里插入图片描述

3.size

返回队列有多少个值
在这里插入图片描述

4.front

取队头的数据,这里就和栈不一样,栈是top,这里要特殊记一下
在这里插入图片描述

5.back

取队尾的数据
在这里插入图片描述

6.push

在这里插入图片描述

7.pop

出数据,这里是先进先出
在这里插入图片描述

插入一个数据

二、相关题目

二叉树的层序遍历:https://leetcode.cn/problems/binary-tree-level-order-traversal/description/
在这里插入图片描述

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution
{
public:
    vector<vector<int>> levelOrder(TreeNode* root) 
    {
        vector<vector<int>> vv;
        queue<TreeNode*> q;
        if(root == nullptr)
        {
            return vv;
        }
        
        q.push(root);

        int leveSzie = 1;
        while(!q.empty())
        {
            vector<int> v;
            while(leveSzie--)
            {
                TreeNode* front = q.front();
               
                v.push_back(q.front()->val);
                 q.pop();
                if(front->left)
                {
                    q.push(front->left);
                }
                if(front->right)
                {
                    q.push(front->right);
                }
            }
            vv.push_back(v);
            leveSzie = q.size(); 
        }
        return vv;
    }
};

三、模拟实现

queue是先进先出,需要头删和尾插,但是呢vector并没有头删函数,主要原因是因为vector头删效率太低了,这里用deque来实现

基本框架

#pragma once
#include<bits/stdc++.h>
template<class T,class Container=deque<T>>
class my_queue
{
private:
	Container _con;
};

完整代码

#pragma once
#include<bits/stdc++.h>
template<class T,class Container=deque<T>> 
class my_queue
{
public:
	void push(const T& x)
	{
		_con.push_back(x);
	}
	void pop()
	{
		_con.pop_front();
	}
	const T& back()
	{
		return _con.back();

	}
	const T& front()
	{
		return _con.front();
	}
	bool empty()
	{
		return _con.empty();
	}
	size_t size()
	{
		return _con.size();
	}
private:
	Container _con;
};

在这里插入图片描述
这里也可以显示实例化给到list,但是给vector就会报错,这里直接避免了低效的使用
在这里插入图片描述

四、deque(双端队列)

deque是一种栈和队列的结合体
从功能上来看,它这里提供了下标访问,也提供了任意位置的插入删除,可以说是结合了栈和队列的优点,但也有缺点

在这里插入图片描述
可以说从功能上是vector和list的结合体

在这里插入图片描述
双端队列的底层差不多就是这样
在这里插入图片描述


缺点:

  1. deque的下标访问是不够极致的效率差了差不多1倍的性能
    在这里插入图片描述
    即使把deque的数据拷贝给vector让vector排序,然后在拷贝回来还是比deque快
    在这里插入图片描述

  2. 头尾删除还好但是如果是插入到中间又是插入到哪里呢又是要挪数据

deque总结:
在这里插入图片描述

五、priority_queue(优先级队列)

在这里插入图片描述
优先级队列底层使用的并不是双端队列,用的是vector,因为它底层是堆(默认就是大堆)需要频繁的用下标去访问


在这里插入图片描述
它的接口和其他几个容器其实差不多,一看就知道干什么


优先级队列的赋值,可以遍历去push
在这里插入图片描述
还有可以用迭代器去给他赋值
在这里插入图片描述


甚至数组都可以用迭代器区间(连续的物理空间)
在这里插入图片描述


默认情况下它是大堆如果要小堆的话就要在初始化的时候去指定
它后面俩个是缺省参数,传入第三个参数的时候第二个也需要传入

在这里插入图片描述


模拟实现

#include<bits/stdc++.h>
using namespace std;
template<class T,class Container = vector<T>>
class my_priority_queue
{
public:
	void adjust_up(int child)
	{
		int parent = (child - 1) / 2;

		while (child > 0) 
		{
			if (_con[parent] < _con[child])
			{
				swap(_con[parent], _con[child]);
				child = parent;
				parent = (child - 1) / 2;
			}
			else
			{
				break;
			}
		}
	}
	void adjust_down(int parent)
	{
		int child = parent * 2 + 1;
		while (child < _con.size())
		{
			if (child + 1 < _con.size() && _con[child ] < _con[child+1])
			{
				++child;
			}

			if (_con[parent] < _con[child])
			{
				swap(_con[parent], _con[child]);
				parent = child;
				child = parent * 2 + 1;
			}
			else
			{
				break;
			}
		}



	}
	template<class InputIterator>
	my_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--)
		{
			adjust_down(i);
		}

	}
	
	void push(const T& x)
	{
		_con.push_back(x);
		adjust_up(_con.size()-1);
	}
	void pop()
	{
		swap(_con[0], _con[_con.size() - 1]);
		_con.pop_back();
		adjust_down(0);
	}
	bool empty()
	{
		return _con.empty();
	}
	size_t size()
	{
		return _con.size();

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


上面的代码是默认大堆的,如果要把它改成小堆就需要去改源代码的符号,非常的麻烦,c语言的qsort改升序还是降序用的是一个函数指针自己写一个比较方法,非常的麻烦

c++给出了一个新的概念仿函数(函数对象)


仿函数

仿函数其实就是重载了运算符
在这里插入图片描述
运算符重载可以直接这样调用
在这里插入图片描述
仿函数其实本质就是个对象


仿函数还可以提供参数自由

在这里插入图片描述


了解了上面的就可以实现以下功能

class myless
{
public:
    bool operator()(const int& x, const int& y)
    {
        return x < y;
    }
};
int main()
{
    myless less1;
    cout << less1(1, 2);
    return 0;
}

在这里插入图片描述

就可以直接这样去比较大小,但我们并不只比较整数,所以这里我们有模板然后显示实例化,灵活性非常高

template<class T>
class myless
{
public:
    bool operator()(const T& x, const T& y)
    {
        return x < y;
    }
};
int main()
{
    myless<int> less1;
    cout << less1(1, 2) << endl;
    myless<string> less2;
    cout << less2("1111", "2222") << endl;
    return 0; 
}

在这里插入图片描述


完整代码

#include<bits/stdc++.h>
using namespace std;
template<class T>
class myless
{
public:
	bool operator()(const T& x, const T& y)
	{
		return x < y;
	}
};
template<class T>
class mygreater
{
public:
	bool operator()(const T& x, const T& y)
	{
		return x > y;
	}
};
template<class T,class Container = vector<T>,class Comapre = myless<T>>
class my_priority_queue
{
public:
	
	void adjust_up(int child)
	{
		Comapre cmp;
		int parent = (child - 1) / 2;

		while (child > 0) 
		{
			if (cmp(_con[parent] ,_con[child]))
			{
				swap(_con[parent], _con[child]);
				child = parent;
				parent = (child - 1) / 2;
			}
			else
			{
				break;
			}
		}
	}
	void adjust_down(int parent)
	{
		Comapre cmp;
		int child = parent * 2 + 1;
		while (child < _con.size())
		{
			if (child + 1 < _con.size() && cmp(_con[child ] , _con[child+1]))
			{
				++child;
			}

			if (cmp(_con[parent] , _con[child]))
			{
				swap(_con[parent], _con[child]);
				parent = child;
				child = parent * 2 + 1;
			}
			else
			{
				break;
			}
		}



	}
	template<class InputIterator>
	my_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--)
		{
			adjust_down(i);
		}

	}
	
	void push(const T& x)
	{
		_con.push_back(x);
		adjust_up(_con.size()-1);
	}
	void pop()
	{
		swap(_con[0], _con[_con.size() - 1]);
		_con.pop_back();
		adjust_down(0);
	}
	bool empty()
	{
		return _con.empty();
	}
	size_t size()
	{
		return _con.size();

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

#include"my_priority_queue.h"
void test1()
{
	vector<int> v = { 1,4,7,2,5,8,3,6,9 };
	priority_queue<int> pq;
	for (auto e : v)
	{
		pq.push(e);
	}
	while (!pq.empty())
	{
		cout << pq.top() << ' ';
		pq.pop();
	}
}
void test2()
{
	vector<int> v = { 1,4,7,2,5,8,3,6,9 };
	priority_queue<int> pq(v.begin(),v.end());
	
	while (!pq.empty())
	{
		cout << pq.top() << ' ';
		pq.pop();
	}
}
void test3()
{
	int arr[] = {1,4,7,2,5,8,3,6,9};
	priority_queue<int,vector<int>,greater<int>> pq(arr ,arr+sizeof(arr)/sizeof(arr[0]));

	while (!pq.empty())
	{
		cout << pq.top() << ' ';
		pq.pop();
	}
}
void test4()
{
	vector<int> v = { 1,4,7,2,5,8,3,6,9 ,0,16};
	my_priority_queue <int,vector<int>,mygreater<int>> pq(v.begin(), v.end());
	while (!pq.empty())
	{
		cout << pq.top() << ' ';
		pq.pop();
	}
}
int main()
{
	test4();
	return 0;
}

总结

队列的底层如果用vector需要头删,头删又需要数据的挪动非常的低效,建议用list或者deque来解决,deque做默认容器还是很不错的

仿函数不仅可以比较大小,还可以用来比较类的大小,大大的提高了灵活性,也是弥补了c语言的一个很大的痛点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值