C++ stack、queue 和 priority_queue 的介绍、使用和模拟实现

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


stack 的介绍和使用

  1. stack 对应数据结构中的栈,其只能从容器的一端进行元素的插入和提取操作(先进后出)

  2. stack 是一种容器适配器,其能够适配的容器有 vector、list 和 deque(双端队列),如果不指定其适配的容器,默认为 deque


stack 的构造函数

stack (const container_type& ctnr = container_type());
//其中 container_type() == deque

stack<int> st1;
stack<int, vector<int>> st2;	//vector 作为底层适配器
stack<int, list<int>> st3;		//list 作为适配器

stack 的成员函数

成员函数作用
bool empty() const判断栈是否为空
size_t size() const获取栈中的元素个数
T& top()获取栈顶元素
void push(const T& val)入栈
void pop()出栈
void swap()交换两个栈中的数据

⭕️注意:由于 stack 的特殊结构(FILO),stack 并没有其他成员函数,也没有迭代器

示例:

#include <stack>
#include <vector>
#include <list>
stack<int> st;
st.push(1);
st.push(2);
st.push(3);
cout << st.top();	//3
st.pop();
cout << st.top();	//2

queue 的介绍和使用

  1. queue 对应数据结构中的队列,其在容器中的一端插入元素,在另一端提取元素(先进先出)

  2. queue 也是一种容器适配器,其能够适配的容器有 vector、list 和 deque(双端队列),如果不指定其适配的容器,默认为 deque


queue 的构造函数

🍀queue 的构造类似于 stack,这里不多赘述

queue (const container_type& ctnr = container_type());

queue<int> que1;
queue<int, vector<int>> que2;
queue<int, list<int>> que3;

queue 的成员函数

成员函数作用
bool empty() const判断队列是否为空
size_t size() const获取队列中的元素个数
T& front()获取队头元素
T& back()获取队尾元素
void pop()对头出队列
void push(const T& val)队尾入队列
void swap()交换两个队列中的数据

⭕️注意:由于 queue的特殊结构(FIFO),queue 并没有其他成员函数,也没有迭代器

示例:

#include <queue>
#include <list>
#include <vector>
queue<int, list<int> que;
que.push(1);
que,push(2);
que.push(3);
que.push(4);
cout << que.front();	//1
cout << que.back();		//4
que.pop();
cout << que.front();	//2 

priority_queue 的介绍和使用

  1. priority_queue 叫做优先级队列,其对应数据结构中的堆,即其构成的二叉树中,每个节点都比其左右子树大(小),根节点为整个堆的最大值(最小值),如下:
    在这里插入图片描述

  2. priority_queue 也是一个容器适配器。数据结构中,堆的二叉树存放在数组中,所以 priority_queue 默认以 vector 容器作为底层适配器。除此之外,deque 也可以作为适配器

  3. 堆有大堆(每个节点都比其左右子树大)和小堆(每个节点都比其左右子树小)之分,默认情况下 priority_queue 为大堆


std::less() 和 std::greater()

🍀在 C++ 标准库中,std::less<T> 和 std::greater<T> 是两个常用的比较函数对象(仿函数),它们用于提供比较操作。它们在<functional> 头文件中定义,并且可以与标准算法一起使用


std::less<T>() 的介绍和使用

  1. std::less<T> 是一个模板结构体,用于定义小于比较。它的主要功能是在两个值之间执行小于操作(即 a < b)。下面是 std::less 的主要特征:

  2. std::less<T> 常用于 STL 容器中排序等的操作。例如,在 list 和 set 容器中,默认情况下使用 std::less<T> 来比较元素,以确保元素按照升序排列

#include <functional>
#include <list>
cout << std::less<int>(3, 4);	//1(true)
cout << std::less<int>(4, 3);	//0(false)
list<int> lst = {1, 4, 3, 2};
lst.sort(less<int>);	//升序排序

//其实 list 的 sort 默认参数就是 less<T>
template <class T>
template <class Compare = bool(const T&, const T&)>
void list<T>::sort(Compare comp = less<T>);

std::less<T> 的模拟实现

namespace yw {
	template <class T>
	struct less {
		operator()(const T& val1, const T& val2)const {
			return val1 < val2;
		}
	};
}

std::greater<T> 的介绍和使用

  1. std::greater<T> 是一个模板结构体,用于定义大于比较。它的主要功能是在两个值之间执行大于操作(即 a > b)

  2. 其用法同 std::less<T>,这里不多赘述

#include <functional>
using namespace std;
cout << greater<int>(1, 2);		//0(false)
cout << greater<int>(2, 1);		//1(true)

list<int> lst = {1, 4, 3, 2};
lst.sort(greater<int>);	//降序排序

std::greater<T> 的模拟实现

namespace yw {
	template <class T>
	struct greater {
		operator()(const T& val1, const T& val2)const {
			return val1 > val2;
		}
	};
}

priority_queue 的构造函数

//默认构造函数
priority_queue (const Compare& comp = Compare(), const Container& ctnr = Container());

//大堆
priority_queue<int> pri_que1;
priority_queue<int, deque<int>> pri_que2;
priority_queue<int, deque<int>, less<int>> pri_que3;
//小堆
priority_queue<int, greater<int>> pri_que4;
priotity_queue<int, deque<int>, greater<int>> pri_que5;


//将 [first, last) 区间元素构成堆
template <class InputIterator>
priority_queue (InputIterator first, InputIterator last, const Compare& comp = Compare(), const Container& ctnr = Container());

vector<int> vtr = {1, 2, 4, 6, 9};
//大堆
priority_queue<int> pri_que1(vtr.begin(), vtr.end());
//小堆
priority_queue<int, greater<int>> pri_que4(vtr.begin(), vtr.end());

priority_queue 的成员函数

常用成员函数

成员函数作用
void push(const T& val)插入到堆中变成叶子节点(并调整成堆)
void pop()取出堆的跟节点(并调整成堆)
T& top()访问堆的根节点元素
size_t size() const返回堆的元素个数
bool empty() const判断堆是否为空
void swap()交换两个堆

示例:

#include <queue>	//priority_queue 在 queue 头文件中
vector<int> vtr = {12, 4, 7, 20, 17, 9, 1};
//大堆
priority_queue<int, vector<int>, less<int>> pri_que(vtr.begin(), vtr.end());

🍀上面代码建堆过程:
在这里插入图片描述

cout << pri_que.top();	//20
pri_que.push(36);

🍀上面代码插入过程:
在这里插入图片描述

cout << pri_que.top();	//36
pri_que.pop();
cout << pri_que.top();	//20

🍀上面代码删除过程:
在这里插入图片描述


emplace()

🍀与 list 容器中的用法一样,这里不多介绍

template <class... Args>
void emplace(Args&&... args);

priority_queue<vector<int>> pri_que;
pri_que.emplace(1, 2, 3, 4, 5);
vector<int> vtr = pri_que.top();
for(auto e : vtr) {
	cout << e << ' ';	
}	//1 2 3 4 5

stack 模拟实现

stack.h 文件

#pragma once
#include <deque>
using std::deque;
namespace yw {
	template <class T, class Container_Type = deque<T>>
	class stack {
	public:
		stack(const Container_Type& cntr = Container_Type()) :_cntr(cntr) {}
		void push(const T& val) {
			_cntr.push_back(val);
		}
		void pop() {
			_cntr.pop_back();
		}
		T& top() {
			return _cntr.back();
		}
		const T& top()const {
			return _cntr.back();
		}
		size_t size()const {
			return _cntr.size();
		}
		bool empty()const {
			return _cntr.empty();
		}
		void swap(stack& st) {
			_cntr.swap(st._cntr);
		}
	private:
		Container_Type _cntr;
	};
}

queue 的模拟实现

queue.h 文件

#pragma once
#include <deque>
using std::deque;
namespace yw {
	template <class T, class Container_Type = deque<T>>
	class queue {
	public:
		queue(const Container_Type& cntr = Container_Type()) :_cntr(cntr) {}
		void push(const T& val) {
			_cntr.push_back(val);
		}
		void pop() {
			_cntr.pop_front();
		}
		T& back() {
			return _cntr.back();
		}
		const T& back()const {
			return _cntr.back();
		}
		T& front() {
			return _cntr.front();
		}
		const T& front()const {
			return _cntr.front();
		}
		size_t size()const {
			return _cntr.size();
		}
		bool empty()const {
			return _cntr.empty();
		}
		void swap(queue& que) {
			_cntr.swap(que._cntr);
		}
	private:
		Container_Type _cntr;
	};
}

priority_queue 的模拟实现

🍀priority_queue 实现的重点是建堆和堆调整算法,这里我们只实现大堆的相关算法

priority_queue.h 头文件


#pragma once
#include <vector>
using std::vector;
namespace yw {
	template <class T, class Container_Type = vector<T>, class Compare = std::less<T>>
	class priority_queue {
	public:
		//构造函数
		priority_queue(const Container_Type cntr = Container_Type(), const Compare comp = Compare())
			:_cntr(cntr)
			, _comp(comp)
		{}
		template <class InputIterator>
		priority_queue(InputIterator first, InputIterator last, const Container_Type cntr = Container_Type(), const Compare comp = Compare()) {
			_cntr = cntr;
			_comp = comp;
			for (InputIterator it = first; it != last; it++) {
				_cntr.push_back(*it);
			}
			//建堆
			BulidHeap(_cntr.size());
		}
		
		void push(const T& val) {
			_cntr.push_back(val);
			AdjustUpHeap(_cntr.size() - 1);
		}
		void pop() {
			::swap(_cntr.front(), _cntr.back());
			_cntr.pop_back();
			AdjustDownHeap(0, _cntr.size());
		}
		T& top() {
			return _cntr.front();
		}
		const T& top()const {
			return _cntr.front();
		}
		size_t size()const {
			return _cntr.size();
		}
		bool empty()const {
			return _cntr.empty();
		}
	private:
		Container_Type _cntr;	//容器适配器
		Compare _comp;			//比较方式
		//建堆
		void BulidHeap(size_t n) {
			for (int start = n / 2; start >= 0; start--) {
				AdjustDownHeap(start, n);
			}
		}
		//向下调整(用于建堆和删除)
		void AdjustDownHeap(int start, size_t n) {
			T val = _cntr[start];
			//start * 2 + 1 是因为 vector 的下标从 0 开始
			for (int i = start * 2 + 1; i < n; i = i * 2 + 1) {
				if (i + 1 < n && _comp(_cntr[i], _cntr[i + 1])) 
					i++;
				}
				if (_comp(val, _cntr[start])) {
					_cntr[start] = _cntr[i];
					start = i;
				}
				else break;
			}
			_cntr[start] = val;
		}
		//向上调整,用于插入
		void AdjustUpHeap(int start, size_t n) {
			for (int i = start; i > 0; i /= 2) {
				if (_comp(_cntr[i / 2], _cntr[i]))
					std::swap(_cntr[i / 2], _cntr[i]);
				else break;
			}
		}
	};
}

🍀说明:由于上面三个容器比较简单,所以这里就不进行测试了

本篇文章到这里就结束了,欢迎批评指正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值