@rg070836rg
2015-08-16T15:09:23.000000Z
字数 3593
阅读 1721
data_structure
三种实现方式,其实就是指,循环队列如何实现判空判满,区别就在这一块,原因是,如果不修改普通队列,会出现二义性,因为空满的状态其实是同一种状态。 下面介绍这三种方式。
通过空出一个位置,解决判空/满的冲突,这是第一次介绍循环队列,附上全部实现:
//模板头文件
template<class T,int MAXSIZE> //定义类模板CirQueue
class CirQueue
{
public:
CirQueue();
~CirQueue();
void EnQueue(T x);//入栈操作,将元素x入栈
T DeQueue(); //出栈操作,将队头元素出队
T GetQueue(); //取队头元素
bool IsFull(); //判断队列是否为满
bool Empty(); //判断队列是否为空
private:
T data[MAXSIZE]; //元素域
int front,rear; //队头和队尾的指针
};
队列,其实很简单,他是只允许在一端进行插入,而在另一端进行删除的运算受限的线性表,所以比较容易掌握。
下面附上具体函数的实现:
/*
* 无参构造函数,
*/
template<class T,int MAXSIZE>
CirQueue<T,MAXSIZE>::CirQueue()
{
front=rear=0;
}
/*
* 入队函数,因为要求在队尾增加元素,
* 所以,如果线性表未满
* 让尾指针向后移,为了不出现假溢出,充分利用空间,
* 所以有取模操作,当到达尾部最大值,则回到0,重新开始。
* 插入时先移动指针,后插入
*/
template <class T,int QueueSize>
void CirQueue<T,QueueSize>::EnQueue(T x)
{
if(IsFull()) //判满
{
cout<<"上溢"<<endl;
return;
}
rear = (rear+1) % QueueSize; //队尾指针在循环意义上加1
data[rear] = x; //在队尾处插入元素
}
/*
* 出队函数,在队头弹出元素,
* 所以,如果线性表不空
* 头指针后移,指向当前的第一个元素,如果当前头指针指向最后一个格子
* 那么进行取模操作,回到0位置。
*/
template <class T,int QueueSize>
T CirQueue<T,QueueSize>::DeQueue()
{
if(Empty()) //判空
{
cerr<<"下溢"<<endl;
}
front = (front+1) % QueueSize; //队头指针在循环意义上加1
return data[front]; //读取并返回出队前的队头元素
}
/*
* 取队头元素,不出队伍。
*/
template <class T,int QueueSize>
T CirQueue<T,QueueSize>::GetQueue()
{
if(Empty()) //判空
{
cerr<<"下溢"<<endl;
exit(0);
}
int i = (front+1) % QueueSize; //队头指针在循环意义上加1
return data[i];
}
/*
* 判空函数
* 如果当两指针重合,队列为空
*/
template <class T,int QueueSize>
bool CirQueue<T,QueueSize>::Empty()
{
return front == rear;
}
/*
* 判满函数
* 如果尾指针逻辑后一格为头指针
* 表示空间满
*/
template <class T,int QueueSize>
bool CirQueue<T,QueueSize>::IsFull()
{
if( (rear+1) % QueueSize == front)
return true;
else
return false;
}
下面对这个函数做个基本的测试。。
int main()
{
CirQueue<int,5> Q;
//塞满整个队列
cout<<"塞满队列。。。"<<endl;
for (int i=0;;i++)
{
if (!Q.IsFull())
{
Q.EnQueue(pow(2,i));
}
else
break;
}
//测试访问头元素
cout<<"取出头元素:";
cout<<Q.GetQueue()<<endl;
//依次出列
cout<<"依次出列:";
while (!Q.Empty())
{
cout<<Q.DeQueue()<<" ";//因为这个函数写了返回值。。
}
return 0;
}
测试结果如图:
为了不浪费一个元素空间,加上一个布尔变量,在入队操作时候把他变为true,如果flag为真,并且front==rear,那么表明队伍满了;在出队的时候,把他设为false,如果最后一个操作为出队,那么又遇到了front==rear,那么表明,这个时候队伍是空的。代码实现如下:
//与上一方式,在这边,仅加了一个bool的控制量
private:
bool flag;
下面列出实现时候,与第一种的区别之处:
template<class T,int MAXSIZE>
CirQueue<T,MAXSIZE>::~CirQueue()
{
}
/*
* 无参构造函数,设置flag为假,保证,初始判断让程序判断为空
*/
template<class T,int MAXSIZE>
CirQueue<T,MAXSIZE>::CirQueue()
{
front=rear=0;
flag=false;
}
/*
* 入队函数,因为要求在队尾增加元素,
* 所以,如果线性表未满
* 让尾指针向后移,为了不出现假溢出,充分利用空间,
* 所以有取模操作,当到达尾部最大值,则回到0,重新开始。
* 插入时先移动指针,后插入
* 同时,让flag置为true
*/
template <class T,int QueueSize>
void CirQueue<T,QueueSize>::EnQueue(T x)
{
if(IsFull()) //判满
{
cout<<"上溢"<<endl;
return;
}
rear = (rear+1) % QueueSize; //队尾指针在循环意义上加1
data[rear] = x; //在队尾处插入元素
flag=true;
}
/*
* 出队函数,在队头弹出元素,
* 所以,如果线性表不空
* 头指针后移,指向当前的第一个元素,如果当前头指针指向最后一个格子,
* 那么进行取模操作,回到0位置。
* 所以队头所指的空间是么有元素的。
* 同时置flag为false
*/
template <class T,int QueueSize>
T CirQueue<T,QueueSize>::DeQueue()
{
if(Empty()) //判空
{
cerr<<"下溢"<<endl;
}
front = (front+1) % QueueSize; //队头指针在循环意义上加1
flag=false;
return data[front]; //读取并返回出队前的队头元素
}
/*
* 取队头元素,不出队伍。
*/
template <class T,int QueueSize>
T CirQueue<T,QueueSize>::GetQueue()
{
if(Empty()) //判空
{
cerr<<"下溢"<<endl;
exit(0);
}
int i = (front+1) % QueueSize; //队头指针在循环意义上加1
return data[i];
}
/*
* 判空函数
* 如果当两指针重合,队列为空
*/
template <class T,int QueueSize>
bool CirQueue<T,QueueSize>::Empty()
{
if ( flag==false && front == rear )
return true;
else
return false;
}
/*
* 判满函数
* 如果尾指针逻辑后一格为头指针
* 表示空间满
*/
template <class T,int QueueSize>
bool CirQueue<T,QueueSize>::IsFull()
{
if ( flag==true && front == rear )
return true;
else
return false;
}
测试函数不做变动,测试结果如下:
用计数器来存储个数,当计数器等于0的时候,空;当计数器等于MAXSIZE的时候,满!
程序很简单,不多说。
/*
* 判空函数
*/
template <class T,int QueueSize>
bool CirQueue<T,QueueSize>::Empty()
{
return count==0;
}
/*
* 判满函数
*/
template <class T,int QueueSize>
bool CirQueue<T,QueueSize>::IsFull()
{
return count==QueueSize;
}
测试结果和方法二同。
三种方法
- 空出一个格子
- 加上flag判定
- 加上计数器
还要注意一点:为了实现循环计数,要掌握下面的语句
- raer=(rear+1)%MAXSIZE
- front=(front+1)%MAXSIZE