C++学习随笔(13)——stack和queue

本章我们来学习一下C++中的stack和queue,由于之前数据结构学过栈和队列等这里就简介一下接口。

目录

1. stack的介绍和使用

1.1 stack的介绍

1.2 stack的模拟实现

2. queue的介绍和使用

2.1 queue的介绍

2.2 queue的使用

2.3 queue的模拟实现

3.1 priority_queue的介绍和使用

3.1 priority_queue的介绍

3.2 priority_queue的使用


1. stack的介绍和使用

1.1 stack的介绍

stack的文档介绍icon-default.png?t=N7T8http://www.cplusplus.com/reference/stack/stack/?kw=stack

1. stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作,可以用数据结构中的栈来理解它。

2. stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定 的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(即栈顶)被压入和弹出。

3. stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器类应该支持以下操作:

         (1)empty:判空操作

         (2)back:获取尾部元素操作

         (3)push_back:尾部插入元素操作

         (4)pop_back:尾部删除元素操作

4. 标准容器vectordeque list均符合这些需求,默认情况下,如果没有为stack指定特定的底层容器, 默认情况下使用deque

函数说明

接口说明

stack()

构造空的栈

empty()

检测stack是否为空

size()

返回stack中元素的个数

top()

返回栈顶元素的引用

push()

将元素val压入stack

pop()

stack中尾部的元素弹出

1.2 stack的模拟实现

从栈的接口中可以看出,栈实际是一种特殊的 vector ,因此使用 vector 完全可以模拟实现 stack
#include<vector>
namespace bite
{
     template<class T>
     class stack
     {
     public:
         stack() {}

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

         void pop() {_c.pop_back();}

         T& top() {return _c.back();}

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

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

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

     private:

         std::vector<T> _c;

     }
}

2. queue的介绍和使用

2.1 queue的介绍

queue的文档介绍icon-default.png?t=N7T8http://www.cplusplus.com/reference/queue/queue/

1. 队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元素,另一端提取元素。

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

3. 底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操 :

        (1) empty:检测队列是否为空

        (2)size:返回队列中有效元素的个数

        (3)  front:返回队头元素的引用

        (4)   back:返回队尾元素的引用

        (5)push_back:在队列尾部入队列

        (6)pop_front:在队列头部出队列

4. 标准容器类dequelist满足了这些要求。默认情况下,如果没有为queue实例化指定容器类,则使用标 准容器deque

2.2 queue的使用

函数声明

接口说明

queue()

构造空的队列

empty()

检测队列是否为空,是返回true,否则返回false

size()

返回队列中有效元素的个数

front()

返回队头元素的引用

back()

返回队尾元素的引用

push()

在队尾将元素val入队列

pop()

将队头元素出队列

2.3 queue的模拟实现

因为 queue 的接口中存在头删和尾插,因此使用 vector 来封装效率太低,故可以借助 list 来模拟实现 queue ,具体如下:
#include <list>
namespace bite
{
     template<class 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:
         std::list<T> _c;
     };
}

3.1 priority_queue的介绍和使用

3.1 priority_queue的介绍

priority_queue文档介绍icon-default.png?t=N7T8http://www.cplusplus.com/reference/queue/priority_queue/

1. 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的,本质上优先级队列类似于数据结构中的堆

2. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元)

3. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,  queue提供一组特定的成员函数来访问其元素。元素从特定容器的尾部弹出,其称为优先队列的顶部。

4. 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:

           (1)empty():检测容器是否为空

           (2)size():返回容器中有效元素个数

           (3)front():返回容器中第一个元素的引用

           (4)push_back():在容器尾部插入元素

           (5)pop_back():删除容器尾部元素

5. 标准容器类vectordeque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指 定容器类,则使用vector

6. 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数 make_heap push_heappop_heap来自动完成此操作。

3.2 priority_queue的使用

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

函数声明

接口说明

priority_queue()/priority_queue(first,  last)

构造一个空的优先级队列

empty( )

检测优先级队列是否为空,是返回true,否则返回 false

top( )

返回优先级队列中最大(最小元素),即堆顶元素

push(x)

在优先级队列中插入元素x

pop()

删除优先级队列中最大(最小)元素,即堆顶元素

注意:

        1. 默认情况下,  priority_queue是大堆。

        2. 如果在priority_queue中放自定义类型的数据,用户需要在自定义类型中提供> < 的重载。

3.4 priority_queue 的模拟实现
通过对 priority_queue 的底层结构就是堆,因此此处只需对对进行通用的封装即可。
#pragma once

#include <iostream>
using namespace std;

#include <vector>
// priority_queue--->堆
namespace myprqueue
{
	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)
		{
			// 将c中的元素调整成堆的结构
			int count = c.size();
			int root = ((count - 2) >> 1);
			for (; root >= 0; root--)
				AdjustDown(root);
		}

		void push(const T& data)
		{
			c.push_back(data);
			AdjustUP(c.size() - 1);
		}

		void pop()
		{
			if (empty())
				return;

			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 AdjustUP(int child)
		{
			int parent = ((child - 1) >> 1);
			while (child)
			{
				if (Compare()(c[parent], c[child]))
				{
					swap(c[child], c[parent]);
					child = parent;
					parent = ((child - 1) >> 1);
				}
				else
				{
					return;
				}
			}
		}

		// 向下调整
		void AdjustDown(int parent)
		{
			size_t child = parent * 2 + 1;
			while (child < c.size())
			{
				// 找以parent为根的较大的孩子
				if (child + 1 < c.size() && Compare()(c[child], c[child + 1]))
					child += 1;

				// 检测双亲是否满足情况
				if (Compare()(c[parent], c[child]))
				{
					swap(c[child], c[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
					return;
			}
		}
	private:
		Container c;
	};
}

void TestQueuePriority()
{
	myprqueue::priority_queue<int> q1;
	q1.push(5);
	q1.push(1);
	q1.push(4);
	q1.push(2);
	q1.push(3);
	q1.push(6);
	cout << q1.top() << endl;

	q1.pop();
	q1.pop();
	cout << q1.top() << endl;

	vector<int> v{ 5,1,4,2,3,6 };
	myprqueue::priority_queue<int, vector<int>,myprqueue::greater<int>> q2(v.begin(), v.end());
	cout << q2.top() << endl;

	q2.pop();
	q2.pop();
	cout << q2.top() << endl;
}

本章待续!

  • 43
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值