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

时间:2021-05-10 17:38:14

 1.1 Stack的实现

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

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

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


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

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

萃取思想:

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

       在把原空间的数据拷贝到新空间时,我们有两种种方法: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的实现

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

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

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

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

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


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

1> 少用一个存储单元

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

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

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

 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;
}