STL第二讲——stack,queue及优先级队列

stack 

stack概述

stack在之前学过,是一种先进后出的的数据结构,我们之前也学过,学习过程中我们知道了stack是可以用链表或顺序表实现出来的,并且在stack中提供的接口有新增元素,移除元素,获得最顶端元素,但是获取元素只能获取stack的最顶端的元素。

stack定义完整列表

因为stack可以在底层使用其他的一些容器来实现,所以这里我们考虑到了可以使用list,vector,还有要新介绍的容器deque。这里在STL源码中是使用了deque这个容器来实现的。

deque介绍

cpp文档查询deque

基本的介绍就不看了,注意deque提供的成员函数,是可以对容器内的元素进行随机访问,头插头删,看起来这是一个完美的容器,好像能解决list不能随机存储,也解决了vector头插的数据移动。

但,真的是这样吗?

这就是deque的基本结构,就是多个同等大小的“vector”链接起来的“list”,但是这个容器有两个最大的问题:一个是在元素内部插入或删除元素,会导致元素移动的问题,造成时间的浪费;其次就是随机访问,deque的通过下标随机访问是计算得出的,就算的过程也不是很简单的,所以多次的随机访问也会造成时间的浪费。

STL中stack的实现

由于stack以底部容器完成其所有工作,这种可以自己选择容器的方法,称为adapter(适配器),这就就是为什么stack不被归类为container(容器),而被归类为contain_adapter

template<class T,class Sequence = deque<T>>
class stack
{
	friend bool operator==__STL_NULL_TMPL_ARGS(const stack&, const stack&);
	friend bool operator< __STL_NULL_TMPL_ARGS(const stack&, const stack&);
public:
	typedef typename Sequence::value_type value_type;
	typedef typename Sequence::size_type size_type;
	typedef typename Sequence::reference reference;
	typedef typename Sequence::const_reference const_reference;
protected:
	Sequence c;//底层容器
public:
	//借助容器c对元素的操作完成stack的操作
	bool empty() const { return c.empty(); }
	size_type size() const { return c.size(); }
	reference top() const { return c.back(); }
	const_reference top() const; { return c.back(); }
	void push(const value_type& x) { c.push_back(x); }
	void pop() { c.pop_back(); }
};

template<class T,class Sequence>
bool operator==(const stack<T, Sequence>& x, const stack<T, Sequence>& y)
{
	return (x.c == y.c);
}
template<class T,class Sequence>
bool operator<(const stack<T, Sequence>& x, const stack<T, Sequence>& y)
{
	return (x.c < y.c);
}

stack中没有迭代器,stack不提供走访功能,也就不提供迭代器。

queue

queue概述

queue是一种先入先出的数据结构,具有两个元素,提供了新增,删除元素,从最底端加入元素,取得最顶端元素,但是除了从最底段增加元素,从最顶端删除元素,queue无法以其他的方式获得元素,也就是说,queue不允许有遍历行为。

queue和stack有很多相似的地方,在实现上也是运用了适配器,可以自定义底层容器,也是默认以deque作为底层容器的。只有在一些函数的实现上有所不同,所以话不多说,直接看源码:

template<class T, class Sequence = deque<T>>
class queue
{
	friend bool operator==__STL_NULL_TMPL_ARGS(const stack&, const stack&);
	friend bool operator< __STL_NULL_TMPL_ARGS(const stack&, const stack&);
public:
	typedef typename Sequence::value_type value_type;
	typedef typename Sequence::size_type size_type;
	typedef typename Sequence::reference reference;
	typedef typename Sequence::const_reference const_reference;
protected:
	Sequence c;//底层容器
public:
	//借助容器c对元素的操作完成queue的操作
	bool empty() const { return c.empty(); }
	size_type size() const { return c.size(); }
	reference front() const { return c.front(); }
	const_reference front() const; { return c.front(); }
	reference back() const { return c.back(); }
	const_reference back() const; { return c.back(); }
	void push(const value_type& x) { c.push_back(x); }
	void pop() { c.pop_front(); }
};

template<class T, class Sequence>
bool operator==(const queue<T, Sequence>& x, const queue<T, Sequence>& y)
{
	return (x.c == y.c);
}
template<class T, class Sequence>
bool operator<(const queue<T, Sequence>& x, const queue<T, Sequence>& y)
{
	return (x.c < y.c);
}

queue中也没有迭代器,和stack一样,queue也是不可以遍历的。

优先级队列priority_queue

priority_queue概述

priority_queue是一个拥有权值观念的queue,它允许加入新元素,移除旧元素,审视元素值等功能。这是一个queue,所以只允许在底端加入元素,并从顶端取出元素。除此之外别无其他存取元素的途径。

权值观念是什么?

就是其内的元素并非按照被推入的次序排列,而是自动依照元素的权值排列,权值最高者排在最前面。

priority_queue也是以底部容器为依据,加上heap处理规则,实现也是很简单的,默认的底部容器是vector。但是增加了一个仿函数,来确定是大堆还是小堆。

 模拟实现一个priority_queue

namespace mypriority_queue
{
	//大堆
	//Compare是比较的仿函数 less->大堆 greater->小堆
	template<class T,class Container = vector<T>,class Compare = std::less<T>>
	class priority_queue
	{
	private:
		Container _con;
	public:
		priority_queue()
		{}
		template <class InputIterator>//迭代器区间的构造函数
		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 adjust_up(size_t child)
		{
			Compare com;
			size_t parent = (child - 1) / 2;
			while (child > 0)
			{
				if (com(_con[parent],_con[child]))
				{
					std::swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}

		void adjust_down(size_t parent)
		{
			Compare com;
			size_t child = parent * 2 + 1;
			while (child < _con.size())
			{
				if (child + 1 < _con.size() &&com(_con[child], _con[child+1]))
				{
					++child;//选出左右孩子中大的那个
				}
				if (com(_con[parent], _con[child]))
				{
					std::swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
					break;
			}
		}

		void push(const T& x)
		{
			_con.push_back(x);
			adjust_up(_con.size() - 1);
		}

		void pop()
		{
			std::swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			adjust_down(0);
		}
		const T& top()
		{
			return _con[0];
		}
		size_t size() const
		{
			return _con.size();
		}
	};
}

priority_queue没有迭代器,priority_queue的所有元素,进出都有一定的规则,只有queue顶端的元素才可以被外界访问。

最后再提一下仿函数这里,仿函数实际就是一个类,重载了“()”,可以让类内使用这个函数重载来达到目的。

内容就到这里啦,大家学习愉快!!

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Feng,

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值