【数据结构】Stack和Queue的模拟实现

 1.1 Stack的实现

栈:一种特殊的线性表,其只允许在固定一端进行插入删除操作,进行插入删除操作的那一端称为栈顶。

它又称为后进先出的线性表。


下面我们就来模拟实现栈:

 要求:利用萃取的方法进行数据拷贝

萃取思想:

       在进行扩容时,先创建新空间,再把原空间的数据拷贝到新空间,再释放旧空间,最后指向新空间。

       在把原空间的数据拷贝到新空间时,我们有两种种方法:1>使用memcpy拷贝元素;2>使用for循环赋值
       memcpy拷贝优点是
效率高,缺点是它是浅拷贝。memcpy函数的功能是从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中
       for循环拷贝的优点是
不管深浅拷贝都不会出错,缺点是效率低
      
为了拷贝时,既能效率高,又能顾忌深浅拷贝,所以我们引入类型萃取。类型萃取是如果栈中保存的是POD(内置数据类型),内置数据类型不用考虑深浅拷贝,那么直接使用memcpy拷贝元素;若不是POD(内置数据类型),则要考虑深浅拷贝,此时使用for循环进行拷贝。


下面直接看代码:

#include<iostream>
using namespace std;
#include<assert.h>
#include "date.h"


struct _TrueType
{
	bool Get()
	{
		return true;
	}
};

struct _FalseType
{
	bool Get()
	{
		return false;
	}
};

template<class TP>
struct TypeTraits	//非内置类型调用模板类
{
	typedef _FalseType _IsPODType;
};

//*************内置类型调用下面对应的模板类的特化*****************
template<>
struct TypeTraits<int>
{
	typedef _TrueType _IsPODType;
};

template<>
struct TypeTraits<unsigned int>
{
	typedef _TrueType _IsPODType;
};

template<>
struct TypeTraits<char>
{
	typedef _TrueType _IsPODType;
};

template<>
struct TypeTraits<unsigned char>
{
	typedef _TrueType _IsPODType;
};


template<>
struct TypeTraits<short>
{
	typedef _TrueType _IsPODType;
};

template<>
struct TypeTraits<unsigned short>
{
	typedef _TrueType _IsPODType;
};


template<>
struct TypeTraits<long>
{
	typedef _TrueType _IsPODType;
};

template<>
struct TypeTraits<unsigned long>
{
	typedef _TrueType _IsPODType;
};

template<>
struct TypeTraits<double>
{
	typedef _TrueType _IsPODType;
};

template<>
struct TypeTraits<long double>
{
	typedef _TrueType _IsPODType;
};

template<>
struct TypeTraits<float>
{
	typedef _TrueType _IsPODType;
};

template<>
struct TypeTraits<long long>
{
	typedef _TrueType _IsPODType;
};

template<>
struct TypeTraits<unsigned long long>
{
	typedef _TrueType _IsPODType;
};

template<>
struct TypeTraits<bool>
{
	typedef _TrueType _IsPODType;
};

//*****************************************************************

//栈的实现
template<class T>
class Stack
{
public:
	Stack(size_t capacity = 10)//构造
		:_size(0)
		,_capacity(capacity)
		,_pData(new T[capacity])
	{}

    Stack(const Stack<T>& s)//拷贝构造-->先创建一个跟s一模一样的空间--->再将s的数据拷贝过来(用萃取)
		:_size(s._size)
		,_capacity(s._capacity)
		,_pData(new T[s._capacity])
	{
		cout<<"Type :"<<typeid(T).name<<endl;//打印栈内存放数据的类型
		if(TypeTraits<T>::_IsPODType().Get())
		{
			memcpy(_pData,s._pData,s._size * sizeof(T));
		}
		else
		{
			for(size_t idx = 0; idx < s._size;idx++)
			{
				_pData[idx] = s._pData[idx];
			}
		}
	}

    Stack<T>& operator=(const Stack<T>& s)//赋值运算符重载
	{
		cout<<"Type :"<<typeid(T).name<<endl;

		if(this != &s)
		{
			if(NULL != _pData)
			{
				delete[] _pData;//销毁原空间
				_pData = new T[s._capacity];//给它重新开辟和s一样大小的新空间
				_capacity = s._capacity;
			}
		
			if(TypeTraits<T>::_IsPODType().Get())//利用类型萃取,把s里的数据拷贝到新空间
			{
				memcpy(_pData,s._pData,s._size * sizeof(T));
			}
			else
			{
				for(size_t idx = 0; idx < s._size;idx++)
				{
					_pData[idx] = s._pData[idx];
				}
			}

			_size = s._size;

		}

		return *this;
	}

	void Push(const T& num)//压栈
	{
		_CheckCapacity();//先检测栈的容量
		_pData[_size] = num;
		_size++;

	}

	void Pop()//出栈
	{
		assert(0 != _size);
		_size--;

	}
	size_t Size()const//栈内有效元素个数
	{
		return _size;
	}

	size_t Capacity()const//栈内容量大小
	{
		return _capacity;
	}

	T& Top()//栈顶元素
	{
		return _pData[_size-1];
	}

	const T& Top()const
	{
		return _pData[size-1];

	}

	bool Empty()const//判断栈是否为空
	{
		return 0 == _size;
	}
	
	~Stack()//析构
	{
		if(_pData)
		{
			delete[] _pData;
			_pData = NULL;
			_size = 0;
			_capacity = 0;

		}

	}

	void Printf()
	{
		for(size_t idx = 0; idx < _size;idx++)
		{
			cout<<_pData[idx]<<" ";
		}
		cout<<endl;
	}


private:
	T* _pData;		//指向栈的指针
	size_t _capacity;//栈的容量
	size_t _size;	//栈内现有元素个数


private:
	void _CheckCapacity()//检查容量,容量不够时增容
	{
		if(_size == _capacity)//因为栈的结构决定了,它只能对栈顶这一个元素进行操作,所以增容出现的情况只能是_size == _capacity
		{
			// 申请空间
			T* temp = new T[_capacity*2+3];//加了一个数,是为了防止构造时传进的_capacity是0
			
			// 拷贝元素
			if(TypeTraits<T>::_IsPODType().Get())//利用类型萃取,把s里的数据拷贝到新空间
			{
				memcpy(temp,_pData,_size * sizeof(T));
			}
			else
			{
				for(size_t idx = 0; idx < _size;idx++)
				{
					temp[idx] = _pData[idx];
				}
			}

			// 释放旧空间
			delete[] _pData;

			// 指向新空间
			_pData = temp;
			_size = _size;
			_capacity = _capacity * 2 + 3;

		}
	}


};


int main()
{
	Date d1(2017,3,3);
	Date d2(2017,5,1);
	Date d3(2017,4,25);
	Stack<Date> s(1);//非内置类型

	s.Push(d1);
	s.Push(d2);
	s.Push(d3);

	Stack<int> s1;//内置类型
	Stack<int> s2(0);
	Stack<int> s3(3);

	s1.Push(1);
	s1.Push(2);
	s1.Push(3);
	s1.Push(4);
	s1.Printf();
	s1.Pop();
	s1.Printf();
	cout<<s1.Top()<<endl;
	cout<<s1.Size()<<endl;
	cout<<s1.Capacity()<<endl;

	s2.Push(1);
	s2.Push(2);
	s2.Push(3);
	s2.Push(4);
	s2.Printf();
	s2.Pop();
	s2.Printf();
	cout<<s2.Top()<<endl;
	cout<<s2.Size()<<endl;
	cout<<s2.Capacity()<<endl;

	return 0;
}


1.2 Queue的实现

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表。

进行插入操作的一端称为队尾,进行删除操作的一端称为队头,简记为头删尾插

其中为了解决顺序队列“假溢出”的问题,我们有了循环队列。

将头尾相接的顺序队列称为循环队列。


循环队列的队列满和队列空的判断有如下三种方式

1> 少用一个存储单元

      如果少用一个存储空间,则让队尾指针加1等于队头指针为队列满的判断条件:(rear+1)%MaxSize == front

      队列空的条件仍旧为rear == front

 2>设置一个标记

      设置标记为flag,初始时置flag = 0;每当入队列操作成功。就置flag = 1;每当出队列成功就置flag =0.

      此时判断队列空的条件为: rear == front && flag ==0;判断队列满的条件为:rear == front && flag == 1


 3>设置一个计数器

      设置计数器count,初始设置count = 0,每当入队列操作成功,就是count加1,每当出队列成功,就使count减1.

      此时队列空的判断条件为:count == 0; 队列满的判断条件为count >0 && rear == front 或者 count == MaxSize 


现在我们用第三种count计数的方法来实现循环队列:

#include<iostream>
using namespace std;
#include<assert.h>

template<class T>
class Queue
{
public:
	Queue(size_t capacity = 5)//构造
		:_capacity(capacity + 3)
		,_count(0)
		,_front(0)
		,_rear(0)
		
	{
		_pData = new T[_capacity];
	}

	void Push(const T& x)//入队
	{
		if(_capacity == _count)//判断队是否已满
		{
			cout<<"队列已满,添加元素失败"<<endl;
			return;
		}

		_pData[_rear] = x;
		_rear = (_rear + 1)%_capacity;
		_count++;
	}

	void Pop()//出队
	{
		if(Empty())//判断队列是否为空
		{
			cout<<"队列为空,删除元素失败"<<endl;
			return;
		}

		_front = (_front + 1)%_capacity;
		_count--;
	}

	T& Front()//返回队首元素
	{
		return _pData[_front];
	}
	
	const T& Front()const
	{
		return _pData[_front];
	}


	T& Back()//返回队尾元素
	{
		return _pData[(_capacity + _rear - 1)%_capacity];
	}

	const T& Back()const
	{
		return _pData[(_capacity + _rear - 1)%_capacity];
	}


	size_t Size()const//队列的长度
	{
		return _count;
	}

	bool Empty()const//判断队列是否为空
	{
		return 0 == _count;
	}


	~Queue()//析构
	{
		if(_pData)
		{
			delete[] _pData;
			_pData = NULL;
		}
	}

private:
	T* _pData;
	size_t _capacity;
	size_t _front;
	size_t _rear;
	size_t _count;
};


int main()
{
	Queue<int> q(0);
	q.Push(1);
	q.Push(2);
	q.Push(3);
	q.Push(4);
	q.Pop();
	cout<<q.Front()<<endl;
	cout<<q.Back()<<endl;
	cout<<q.Size()<<endl;
	q.Pop();
	q.Pop();
	if(q.Empty())
	{
		cout<<"队列为空"<<endl;
	}

	return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值