C++ STL Queue,Stack底层实现分析

        C++容器适配器(Adapter) stackqueue是非常常用的STL容器.队列与栈也是比较基础的数据结构类型.今天我们来实现一个stack与一个queue.

目录

基本结构

为什么是Deque?

源码实现


基本结构

        stack也就是栈,遵循的是先进后出原则.queue也就是队列,遵循的是先进先出的原则. 作为基本数据结构的队列与栈没有什么难点.

        此外,stack与queue不能遍历,不能随机访问,不能查询中间结点,不能插入中间结点,只能做与其性质对应的两端结点的操作.

        STL中的stack与queue头文件源码的长度在包括大量注释的情况下分别为306行与572行.这在STL中算是比较简短的源码了.之所以可以做到如此简短,除了其本身功能简单的原因外,也因为此两者并非从0建立的容器,它们有一个共同的底层容器:Deque.

为什么是Deque?

        由于stack与queue只需要进行两端的存取操作,所以只需要能实现一些基础方法如push_back(),push_front(),back(),front()等就可以满足二者的实现.所以事实上无论是list,vector还是deque,都可以实现队列与栈的操作.那么为什么STL采用了Deque?

        首先是vector.由于其顺序表结构的局限性,vector在首端的插入操作时间复杂度就已经高到了O(n)的程度.此外,stack与queue只需要端结点的操作,使用vector实现必然会浪费vector本身随机存取的优势.最后,我个人认为在使用vector这种简单顺序结构实现queue时,是存在先天不足的.因为queue的特点决定了由于其弹出队头与插入队尾是同一个方向,在多次操作后必然会使数据整体位移导致越界.使用vector实现队列必然要考虑这个问题,解决的办法不论是循环队列还是频繁地释放与开辟空间都会导致代码的无谓复杂化.上面的原因导致vector根本不是合适的stack/queue基础容器.

        其次是list.作为双向循环列表的list满足stack与queue的端结点操作,并且其时间复杂度也能达到O(1)的程度,所以事实上也是不错的选择.但是在实践中其速度与内存占用依旧是没有deque优秀的,个人猜测是因为list的循环特性在stack与queue中没有得到利用,所以在实践中与deque相比产生了常数级别的实践复杂度劣势和空间复杂度劣势.

        阐明了上述两个容器在实现stack与queue时的不足,我们来介绍deque的特性.deque是一个双向队列,可以操作端结点,且这些操作具有O(1)级别的时间复杂度.但deque并非简单的链式或顺序结构,而是通过一个map索引表连接多个顺序表来存取数据的,它的具体实现今天不做具体讨论,改日补充.deque的缺陷在于虽然它支持随机访问和中间结点存取,但它的这些功能性能非常差.当然,这些并不会影响它作为stack与queue的基础容器的性能.

源码实现

        由于引入deque后,stack与queue的实现就非常简单了.因此直接给出二者的实现源码:

        MyQueue:

#ifndef MYQUEUE_H
#define MYQUEUE_H
#include 
using namespace std;
template>
class MyQueue{
    protected:	
    	deque c;
    public:
    	void pop(){
    		c.pop_front();
		}
		void push(const T&val){
			c.push_back(val);
		}
		size_t size(){
			return c.size();
		}
		T& top(){
			return c.back();
		}
		bool empty(){
			return c.empty();
		}
};
#endif

        MyStack: 

#ifndef MYSTACK_H
#define MYSTACK_H
#include 
using namespace std;
template>
class MyStack{
    protected:			//受保护的的 
    	deque c;
    public:
    	void pop(){
    		c.pop_back();
		}
		void push(const T&val){
			c.push_back(val);
		}
		size_t size(){
			return c.size();
		}
		T& top(){
			return c.back();
		}
		bool empty(){
			return c.empty();
		}
};
#endif

        二者只有pop()方法的删除位置区别.

        补充一点C++基础知识:protected声明的成员,其作用范围大于private,小于public.即:private与private内部的成员相比,protected支持本类的派生类访问保护成员.

你可能感兴趣的:(源码分析,c++,数据结构,stl)