@rg070836rg
2015-08-16T07:09:23.000000Z
字数 3593
阅读 1977
data_structure
三种实现方式,其实就是指,循环队列如何实现判空判满,区别就在这一块,原因是,如果不修改普通队列,会出现二义性,因为空满的状态其实是同一种状态。 下面介绍这三种方式。
通过空出一个位置,解决判空/满的冲突,这是第一次介绍循环队列,附上全部实现:
//模板头文件template<class T,int MAXSIZE> //定义类模板CirQueueclass 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; //队尾指针在循环意义上加1data[rear] = x; //在队尾处插入元素}/** 出队函数,在队头弹出元素,* 所以,如果线性表不空* 头指针后移,指向当前的第一个元素,如果当前头指针指向最后一个格子* 那么进行取模操作,回到0位置。*/template <class T,int QueueSize>T CirQueue<T,QueueSize>::DeQueue(){if(Empty()) //判空{cerr<<"下溢"<<endl;}front = (front+1) % QueueSize; //队头指针在循环意义上加1return data[front]; //读取并返回出队前的队头元素}/** 取队头元素,不出队伍。*/template <class T,int QueueSize>T CirQueue<T,QueueSize>::GetQueue(){if(Empty()) //判空{cerr<<"下溢"<<endl;exit(0);}int i = (front+1) % QueueSize; //队头指针在循环意义上加1return 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;elsereturn false;}
下面对这个函数做个基本的测试。。
int main(){CirQueue<int,5> Q;//塞满整个队列cout<<"塞满队列。。。"<<endl;for (int i=0;;i++){if (!Q.IsFull()){Q.EnQueue(pow(2,i));}elsebreak;}//测试访问头元素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; //队尾指针在循环意义上加1data[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; //队头指针在循环意义上加1flag=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; //队头指针在循环意义上加1return data[i];}/** 判空函数* 如果当两指针重合,队列为空*/template <class T,int QueueSize>bool CirQueue<T,QueueSize>::Empty(){if ( flag==false && front == rear )return true;elsereturn false;}/** 判满函数* 如果尾指针逻辑后一格为头指针* 表示空间满*/template <class T,int QueueSize>bool CirQueue<T,QueueSize>::IsFull(){if ( flag==true && front == rear )return true;elsereturn 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