C++STL详解2:stack和queue

C++STL详解2:stack和queue


递归何不归:个人主页
个人专栏 : 《C++庖丁解牛》《数据结构详解》

在广袤的空间和无限的时间中,能与你共享同一颗行星和同一段时光,是我莫大的荣幸


一、stack和queue

1.1容器适配器

在STL中,stack和queue包括priority_queue其实都不是独立的容器而是其他容器封装得来的,我们将他们称为容器适配器。

1.2 stack

1.2.1 stack的简介

stack中文名为栈,遵循先进后出原则 ,是一个被广泛使用的数据结构。

这是它的接口:

1.2.2 stack的简单实现

由于我们要实现的是栈,只需要在一端进行操作 ,所以我们可以将vector封装,然后在vector的尾部操作,此时可以做到效率最大化

我们实现的栈默认是将vector封装,故在模版声明处给出缺省值

cpp 复制代码
template<class T, class Con = vector<T>>
class stack
{
public:
    stack()
    {
        Con();
    }
    void push(const T& t)
    {
        _con.push_back(t);
    }
    void pop()
    {
        _con.pop_back();
    }
    T& top()
    {
        return _con.back();
    }
    const T& top()const
    {
        return _con.back();
    }
    size_t size()const
    {
        return _con.size();
    }
    bool empty()const
    {
        return _con.empty();
    }
private:
    Con _con;
};

1.3 queue

1.3.1 queue的简介

队列是一种**先进先出(FIFO)**的数据结构

这是他的接口

1.3.2queue的简单实现

由于队列是先进先出的的数据结构 ,使用vector时难免会出现挪动数据的低效行为 ,所以这里我们封装可以自由插入和删除的list来实现队列

cpp 复制代码
template<class T, class Con = list<T>>
class queue
{
public:
    queue()
    {
        Con _con;
    }
    void push(const T& x)
    {
        _con.push_front(x);
    }
    void pop()
    {
        _con.pop_back();
    }
    T& back()
    {
        return _con.back();
    }
    const T& back()const
    {
        return _con.back();
    }
    T& front()
    {
        return _con.front();
    }
    const T& front()const
    {
        return _con.front();
    }
    size_t size()const
    {
        return _con.size();
    }
    bool empty()const
    {
        return _con.empty();
    }
private:
    Con _con;
};

1.4 deque详解


我们之前在实现stack和queue的时候分别使用了vector 和 list,但是如果我们仔细观查他们的函数声明就会发现:

库函数中的stack和queue都是使用一个叫deque的容器封装的

那这个deque是何方神圣呢?

1.4.1deque简介

我们先查一下文档:
deque(这里图片比较长,就直接挂链接了)
我们可以发现:deque包含了vector和list的所有功能

但这又是怎么实现的呢?

1.4.2 结构

其实,deque就是一个缝合怪 ,他身上既有vector 的影子,也有list的影子

可以看到,deque 的本体是一个指针数组,指针数组中的指针指向等大的空间

1.4.2.1 deque容器的成员变量


1.4.2.2头插和尾插操作

当deque需要头插时,就在中控数组前面再添加一个数组并插入 ,需要在尾部插入时,就在尾部的数组内插入,如果已满,就再添加一个新的数组

我们这里只是将一个大概,具体的在下面讲

1.4.2.1支不支持下标访问

deque是支持下标访问的,但是效率相较于vector 来说是较低的 (毕竟deque中的迭代器的移动逻辑是比较复杂的

可以看出,deque这个结构保留了stack内存连续、缓存利用率高和支持下标访问的优点 ,也保留了list头插不需要挪动数据的优点,可谓是真正的缝合怪。

1.4.3 迭代器的组成和工作原理

1.4.3.1 组成
  • first:指向数组的开头
  • last:指向数组的结尾
  • cur:指向现在的位置
  • node:指向中控数组中的指针位置
1.4.3.2 移动逻辑

我们先看源码

分情况考虑:

  • cur<last:连续地址,直接++
  • cur==last:开一段新的数组,然后移动迭代器,再将数据写入

1.4.4 头插尾插和中间插入

尾插没什么好说的,迭代器++即可

1.4.4.1头插

头插的操作相对来说还是难一点,如果start指向的区域是满的(cur==last) ,就会在start之前 开一个数组,并在start指向的数组中从后往前(即从last位置开始放置)放置数据

具体的代码还是比较难解释的,所以这里只说思路

1.4.4.2中间插入

中间插入操作就更复杂了,因为deque的数据还是存储在vector的顺序结构中 ,中间插入不可避免的需要挪动数据 ,时间复杂度是O(N)

1.4.5 优缺点分析

  • 1、deque头插尾插还是挺高效的(vector头插需要挪动数据 ,list 每次都要申请空间,而deque可以一次申请一块空间
  • 2、支持下标随机访问,但是效率低于vector
  • 2、中间插入数据效率很低,要挪动数据,时间复杂度是O(N)

二、仿函数

2.1仿函数的概念

仿函数(Functor) 是一个重载了 operator() 的类或结构体。它的实例可以像函数一样被调用,因此也被称为函数对象

2.2 仿函数的使用

cpp 复制代码
template<class T>
class less_func
{
public:
	bool operator()(const T& a,const T& b)
	{
		return a < b;
	}
};
jyy::less_func<int> LessFunc;
cout << LessFunc(1, 2) << endl;
//	cout << LessFunc.operator()(1, 2) << endl;

注意,这里的仿函数的声明处不要忘记加上public:

三、priority_queue(优先级队列)

优先级队列的底层就是堆默认的顺序是大的数据优先级高(即大堆)

3.1 优先级队列的简单介绍

我们可以看到,优先级队列Compare的缺省值是仿函数less(即<)

cpp 复制代码
int main()
{
	//jyy::test_queue1();
	//这里是传了一个仿函数,用于指示容器按照相应的方式
	priority_queue<int,vector<int> > pq;
	pq.push(1);
	pq.push(4);
	pq.push(3);
	pq.push(5);
	pq.push(6);
	pq.push(9);
	while (!pq.empty())
	{
		cout << pq.top() << " ";
		pq.pop();
	}
	cout << endl;
	return 0;
}

运行可得:

可以发现:使用仿函数less的时候,组成的是大堆

我们将仿函数换成greater试试

cpp 复制代码
	priority_queue<int,vector<int>,greater<int> > pq;

得出:

可知建的是小堆

3.2 实现

今天实在是燃尽了,这里就不细讲了,啥时候再说吧

cpp 复制代码
#pragma once
#include<iostream>
#include<vector>
#include<algorithm>
#include<cassert>
using namespace std;
namespace jyy
{
	template<class T>
	class less_func
	{
	public:
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};
	template<class T>
	class greater_func
	{
	public:
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};


	template<class T ,class Con = vector<T> ,class Compare = less_func<T>>
	class priority_queue
	{
	public:
		
		friend class greater_func<T>;
		friend class less_func<T>;

		priority_queue()
		{
			Con();
		}

		template <class InputIterator>

		priority_queue(InputIterator first, InputIterator last)
		{
			Con(first, last);
		}

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

		size_t size() const
		{
			return _con.empty();
		}

		const T& top() const
		{
			return _con[0];
		}

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

		void pop()
		{
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			AdjustDown(0);
		}


	private:
		Con _con;
		Compare _compare;
		void AdjustUp(size_t child)
		{
			assert(child < _con.size());
			int  parent = (child - 1) / 2;
			
			//先是建大堆,那就是如果child 如果是child大就向上调整
			while (parent >= 0)
			{
				
				//if(_con[parent] < _con[child])
				if (_compare(_con[parent], _con[child]))
				{
					swap(_con[parent], _con[child]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
				
			}

		}
		void AdjustDown(size_t parent)
		{
			assert(parent >= 0);
			//这里是建大堆,向下调整肯定是先找到下面的较小的值
			size_t child = parent  * 2 + 1;
			
			while (child < _con.size())
			{
				if (parent * 2 + 2 < _con.size() && _compare(_con[parent * 2 + 1], _con[parent * 2 + 2]))
				{
					child = parent * 2 + 2;
				}
				//if(_con[parent]<_con[child])
				if (_compare(_con[parent], _con[child]))
				{
					swap(_con[parent], _con[child]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					//这里还是说明符合规则
					break;
				}

			}
		}
	};
}
相关推荐
宵时待雨2 小时前
C++笔记归纳11:多态
开发语言·c++·笔记
小道士写程序2 小时前
Babylon.js WebGPU Ocean Demo — 完整踩坑记录
开发语言·javascript·ecmascript
Code知行合壹2 小时前
JDK10新特性
开发语言·jdk
qq_390760392 小时前
简单的线程安全日志记录器
开发语言·数据库·c#
T1an-12 小时前
C++11智能指针shared_ptr的控制块内都有什么?
开发语言·c++
迈巴赫车主2 小时前
天梯赛 L2-004 这是二叉搜索树吗?java
java·开发语言·数据结构·算法·天梯赛
小鸡吃米…2 小时前
基准测试与性能分析
开发语言·python
神仙别闹2 小时前
基于MATLAB实现(GUI)汽车出入库识别系统
开发语言·matlab·汽车
今儿敲了吗2 小时前
python基础学习笔记第一章
开发语言·python