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