C++——stack和queue

发布于:2024-10-12 ⋅ 阅读:(6) ⋅ 点赞:(0)

目录

前言

一、接口

二、 模拟实现

三、deque双端队列(了解)

1.大致思路

2.迭代器

3.缺陷

4.为什么选择deque作为queue和stack的默认容器


前言

        不管是stack还是queue,我们都可以通过调用vector来对其进行模拟实现,实际上库里面也是这么做的,只需要在vector的外层套一层娃即可实现stack和queue。

        其实,实现stack和queue不一定必须要用vector,也可以用list,这样就会影响它的底层。比如有些时候,链式栈更能解决需求,底层就用list,顺序栈更能解决需求,就用vector。

        所以在实现stack和queue的过程中,我们除存储数据类型外要新加一个模板参数来传容器,再给这个容器一个初始值,那么这就是容器适配器。

一、接口

        这些接口看名字就能秒懂,在之前学过的容器基础上,这些已经很简单了。

stack

queue

 

二、 模拟实现

        stack与queue的实现都用了复用其它容器的手段,所有接口基本上也就是一行搞定

        实现过程中容器默认是deque,后面我会简单介绍一下这个容器。

stack.h

#pragma once
#include <deque>
namespace muss
{
	template<class T, class Container = deque<T>>
	class stack
	{
	public:
		bool empty()
		{
			return _con.empty();
		}

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

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

		void push(const T& value)
		{
			_con.push_back(value);
		}

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

		void swap(const queue<T>& q)
		{
			std::swap(_con, q._con);
		}
	private:
		Container _con;
	};

	void stack_test1()
	{
		stack<int> q;
		cout << q.empty() << endl;
		q.push(1);
		q.push(2);
		q.push(3);
		q.push(4);
		cout << q.size() << endl;
		cout << q.top() << endl;
		q.pop();
		cout << q.top() << endl;
		q.pop();
		q.pop();
		cout << q.size() << endl;
		q.pop();
		cout << q.size() << endl;
		cout << q.empty() << endl;
	}

	void stack_test2()
	{
		stack<int> q1;
		q1.push(1);
		q1.push(2);
		q1.push(3);
		q1.push(4);
		stack<int> q2;
		q2.push(11);
		q2.push(22);
		q2.push(33);
		swap(q1, q2);

	}
}

queue.h

#pragma once
#include <deque>
namespace muss
{
	//             数据类型   queue的底层,默认是deque,双端队列,链表和顺序表的缝合怪
	template<class T, class Container = deque<T>>
	class queue
	{
	public:
		bool empty()
		{
			return _con.empty();
		}

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

		T& front()
		{
			return _con.front();
		}

		T& back()
		{
			return _con.back();
		}

		void push(const T& value)
		{
			_con.push_back(value);
		}

		void pop()
		{
			_con.pop_front();
		}

		void swap(const queue<T>& q)
		{
			std::swap(_con, q._con);
		}

	private:
		Container _con;
	};

	void queue_test1()
	{
		queue<int> q;
		cout << q.empty() << endl;
		q.push(1);
		q.push(2);
		q.push(3);
		q.push(4);
		cout << q.size() << endl;
		cout << q.front() << endl;
		cout << q.back() << endl;
		q.pop();
		q.pop();
		cout << q.front() << endl;
		cout << q.back() << endl;
		q.pop();
		cout << q.size() << endl;
		q.pop();
		cout << q.size() << endl;
		cout << q.empty() << endl;
	}

	void queue_test2()
	{
		queue<int> q1;
		q1.push(1);
		q1.push(2);
		q1.push(3);
		q1.push(4);
		queue<int> q2;
		q2.push(11);
		q2.push(22);
		q2.push(33);
		swap(q1, q2);

 	}
}

test.cpp

#include <iostream>

using namespace std;

#include "queue.h"
#include "stack.h"

int main()
{
	//muss::queue_test2();
	muss::stack_test2();

	return 0;
}

一共也没几行

三、deque双端队列(了解)

        总所周知,vector与list各有优势,vector强在它的随机访问效率极高,但是插入删除效率很低,而list不能支持随机访问,但是插入删除效率很高。所以,有人想着能不能把vector与list整合到一块去,使这个具有两者的优势,于是deque诞生了。

1.大致思路

        把list的一个一个结点改为储存数据的定长数组,再额外搞出一个中控数组来储存各个定长数组的地址。这样,扩容只需要这个中控数组扩容,不需要挪动数据。

        可以在头尾两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率比较高

        deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组

2.迭代器

        双端队列底层是一段假象的连续空间,实际是分段连续的,为了维护其“整体连续以及随机访问的假象,落在了deque的迭代器身上,因此deque的迭代器设计就比较复杂。

        它的迭代器封装了四个指针,cur是当前位置的指针,first和last分别是当前数组(结点)首位元素的指针,node是当前数组(结点)的地址,在中控数组中,依靠这四个指针,它们才能实现遍历操作。

3.缺陷

        与vector比较deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在容时,也不需要搬移大量的元素,因此其效率是必vector高的。

        与list 比较 ,其底层是连续空间, 空间利用率比较高 ,不需要存储额外字段。
        但是,deque 有一个致命缺陷:不适合遍历,因为在遍历时, deque 的迭代器要频繁的去检测其 是否移动到某段小空间的边界,导致效率低下 ,而序列式场景中,可能需要经常遍历,因此 在实
际中,需要线性结构时,大多数情况下优先考虑 vector list deque 的应用并不多,而 目前能看
到的一个应用就是, STL 用其作为 stack queue 的底层数据结构

4.为什么选择deque作为queue和stack的默认容器

stack 是一种后进先出的特殊线性数据结构,因此只要具有 push_back() pop_back() 操作的线性
结构,都可以作为 stack 的底层容器,比如 vector list 都可以; queue 是先进先出的特殊线性数据
结构,只要具有 push_back pop_front 操作的线性结构,都可以作为 queue 的底层容器,比如
list 。但是 STL 中对 stack queue 默认选择 deque 作为其底层容器,主要是因为:
1. stack queue 不需要遍历 ( 因此 stack queue 没有迭代器 ) ,只需要在固定的一端或者两端进
行操作。
2. stack 中元素增长时, deque vector 的效率高 ( 扩容时不需要搬移大量数据 ) queue 中的
元素增长时, deque 不仅效率高,而且内存使用率高。
结合了 deque 的优点,而完美的避开了其缺陷。

okkkkkkkkkk,完结撒花~~~~