之前讲的是基于链式存储的队列,如果是基于数组的存储结构 的队列会是怎样的呢?起初队头和队尾均指向第一个元素,随着不断地进队列,后来队列满了。这时候从队头出队列,当出完后此时队头和队尾均指向数组的末尾了。虽然这个数组是空的,但是不能用来存储了,造成了很大的浪费。在这个时候能不能将数组前面的空位置利用起来,继续用来存储呢?这个时候就用到了循环队列的概念了,循环队列主要是为了解决基于顺序存储的队列造成的大量空间浪费问题,这时候一个数组被看成了一环,这样子之前的情况下队尾值可以继续进元素的,把空间利用起来了。
将一个数组看成是一个环用到了取模运算,如(rear + 1)%maxSize; (front + 1)%maxSize等式子。基于数组的存储结构一般有静态开辟空间和动态开辟空间,这里使用静态开辟方式。链式存储的队列有空队列概念没有满队列概念,可是这里的循环队列是有空队列和满队列概念的。空队列的时候就是队头指针和队尾指针重合,可是队列满怎么判断呢?在这里为了简便判断牺牲了一个数组元素空间,当(rear + 1)%maxSize == front成立的时候就是循环队列满的时候,也就是说当队尾和队头空一个位置的时候就是队列满的情况了。
由于将数组处理成了一个环状结构了,所以这个时候队列的头与尾也就不那么重要了,一般情况下默认为空队列队头和队尾同时指向数组的第一个位置。为了易于扩展和方便使用,这里采取了事先指定队头和队尾的方式,当然可以是默认的。队头指向队列的第一个真实存在的元素,队尾指针指向的是队列中实际最后一个元素的下一个位置。这里和STL的容器是一个道理,都是左闭右开区间。
//基于数组的队列 拥有front与rear队首队尾标志,但不能实现空间的合理利用 //基于数组的循环队列 可以实现空间的合理利用 #pragma once #include<iostream> using namespace std; const int MAXSIZE = 10; const int begin_front = 0; const int begin_rear = 0; template<class T> class Queue { protected: T data[MAXSIZE];//基于数组 限定最大程度 int front;//队首“指针” int rear;//队尾“指针” 实则为一个下标 public: Queue(); Queue(Queue<T> &Q); ~Queue(); void ClearQueue();//置为空队列 void InQueue(T &elem);//插入队尾 T DeQueue();//删除队尾,返回元素值 之后被后来元素值 覆盖 T DeQueue(int elem_index);//重载 用于深复制时的按下表提取 int Length();//求当前循环队列的长度 int GetFront();//返回 队首 “指针” int GetRear();//返回 队尾 “指针”,实则为一个 整型下标值 bool isEmpty();//盘空循环队列 bool isFull();//判满循环队列 void operator= (Queue<T> &Q); void output();//一次性 按顺序输出队列 }; template<class T> Queue<T>::Queue() { front = begin_front;//初始化循环队列,使其首尾指针都置为0 rear = begin_rear; } template<class T> Queue<T>::Queue(Queue<T> &Q) { front = begin_front;//初始化 rear = begin_rear; T elem;//临时存储变量 int current = Q.GetFront();//提取Q队列的队首“指针” int Q_REAR = Q.GetRear();//提取Q队列的队尾“指针” while(current != Q_REAR) { elem = Q.DeQueue(current); data[rear] = elem; current = (current + 1) % MAXSIZE;//记录指针 后移 rear = (rear + 1) % MAXSIZE;//当前队列的尾指针 后移 } } template<class T> Queue<T>::~Queue() { //delete []data; } template<class T> void Queue<T>::ClearQueue() { //delete []data;//连续空间的指针为data 直接delete掉 全部销毁 } template<class T> void Queue<T>::InQueue(T &elem)//插入 先判是否循环队列 满 { if((rear + 1) % MAXSIZE == front)//尾指针向下转一位到达首指针 { cout<<"循环队列满"<<endl; return ; } data[rear] = elem;//插入队尾 rear = (rear + 1) % MAXSIZE;//队尾指针后移一位 } template<class T> T Queue<T>::DeQueue()//队首 出 { T elem; if(rear == front)//空队列 { cout<<"空队列"<<endl; return ; } elem = data[front];//提取队首元素 front = (front + 1) % MAXSIZE;//队首指针 同样后移 return elem; } template<class T> T Queue<T>::DeQueue(int elem_index)//按下标值 提取 { return data[elem_index];//此下表值 是无误的 合法的 } template<class T> int Queue<T>::GetFront() { return front; } template<class T> int Queue<T>::GetRear() { return rear; } template<class T> int Queue<T>::Length()//求当前队列 长度 { return ((rear - front+ MAXSIZE) % MAXSIZE); } template<class T> bool Queue<T>::isEmpty() { return ((rear == front) ? true : false);//逗号运算符 } template<class T> bool Queue<T>::isFull() { return ((((rear + 1) % MAXSIZE) == front) ? true : false); } template<class T> void Queue<T>::operator= (Queue<T> &Q) { front = begin_front;//先初始化 rear = begin_rear; T elem;//临时存储变量 int current = Q.GetFront();//提取Q队列的队首“指针” int Q_REAR = Q.GetRear();//提取Q队列的队尾“指针” while(current != Q_REAR) { elem = Q.DeQueue(current); data[rear] = elem; current = (current + 1) % MAXSIZE;//记录指针 后移 rear = (rear + 1) % MAXSIZE;//当前队列的尾指针 后移 } } template<class T> void Queue<T>::output() { int current = front;//当前指针 指向队首 开始遍历 while(current != rear) { cout<<"#"<<current+1<<":"<<data[current]<<endl; current = (current + 1) % MAXSIZE; } }
下面是主函数测试:
#include"Queue.h" #include<iostream> using namespace std; void main() { Queue<int> qu; for(int i=0;i<5;i++) qu.InQueue(i); qu.output(); Queue<int> qu1; qu1 = qu; qu1.output(); }