您的位置:首页 > 理论基础 > 数据结构算法

数据结构之循环队列

2017-10-14 17:56 253 查看
一.引入

        假设线性表有 n 个数据元素,顺序表要求把表中的所有元素都存储在数组的前 n 个单元。假设队列有 n 个数据元素,顺序存储的队列也应该把队列的所有元素都存储在数组的前 n 个单元。如果把队头元素放在数组中下标为 0 的一端,则入队操作的时间开销仅为 O(1) ,此时的入队操作相当于追加,不需要移动元素;但是出队操作的时间开销为 O(n) ,因为要保证剩下的 n-1 个元素仍然存储在数组的前 n-1 个单元,所有元素都要向前移动一个位置。

        如果放宽队列的所有元素必须存储在数组的前 n 个单元这一条件,只要求队列的元素存储在数组中连续的位置,就可以得到一种更为有效的存储方法,此时入队和出队操作的时间开销都是 O(1) ,因为没有移动任何元素,但是队列的队头和队尾都是活动的,因此,需要设置队头、队尾两个指针,并且约定:队头指针 front 指向队头元素的前一个位置,队尾指针 rear 指向队尾元素。

        但是这种方法有一个新的问题。随着队列的插入和删除操作的进行,整个队列向数组中下标较大的位置移过去,从而产生了队列的 “单向移动性” 。当元素被插入到数组中下标最大的位置上之后,队列的空间就用尽了,尽管此时数组的低端还有空闲空间,这种现象叫做 “假溢出” 。

        解决假溢出的方法是将存储队列的数组看成是头尾相接的循环结构,即允许队列直接从数组中下标最大的位置延续到下标最大的位置延续到下标最小的位置。这通过取模操作很容易实现。队列的这种头尾相接的顺序存储结构成为循环队列。

二.算法设计

CirQueue.h

#ifndef SRC_CIRQUEUE_H_
#define SRC_CIRQUEUE_H_

template <class T>
class CirQueue {
public:
CirQueue();//无参构造器
CirQueue(T array[],int length);//有参构造器
virtual ~CirQueue();//析构函数
void enqueue(T value);//入队
T dequeue();//出队
T getHead();//取队头元素
void getAll();//遍历队的全部元素
bool isEmpty();//判断是否队空
bool isFull();//判断是否队满
private:
static const int MAX_LENGTH = 6;//队的最大长
4000
度,因为有一个空间要空出来,所以实际存储元素的长度为(队的最大长度-1)
T data[MAX_LENGTH];//队元素
int front;//队头指针,指向队头元素的前一个位置
int rear;//队尾指针,指向队尾元素
};

#endif
三.详细设计(C++)

CirQueue.cpp

#include "CirQueue.h"
#include <iostream>
using namespace std;

/*
* 无参构造器:
* 1.队头指针 front 指向队元素的末尾
* 2.队尾指针 rear 指向队元素的末尾
*/
template <class T>
CirQueue<T>::CirQueue(){
front = MAX_LENGTH-1;
rear = MAX_LENGTH-1;
}
/*
* 有参构造器:
* 1.队头指针 front 指向队元素的末尾
* 2.队尾指针 rear 指向队元素的末尾
* 3.判断数组的长度是否大于队的实际存储元素的长度(即队的最大长度-1),若是则返回,否则
* 4.循环,初始化队元素
* 5.队尾指针 rear 指向初始化了的队元素的末尾
*/
template <class T>
CirQueue<T>::CirQueue(T array[],int length){
front = MAX_LENGTH-1;
rear = MAX_LENGTH-1;
if(length > MAX_LENGTH-1){
cout<<"数组长度过大,当前队的最大长度为:"<<(MAX_LENGTH-1)<<"!"<<endl;
return;
}
for(int i = 0;i < length;i++){
data[i] = array[i];
}
rear = length-1;
}
/*
* 析构函数:
* 空的
*/
template <class T>
CirQueue<T>::~CirQueue(){
}
/*
* 入队:
* 1.判断是否队满,若是则返回,否则
* 2.队尾指针 rear 指向队尾的下一个位置
* 注意:因为是循环结构,所以队尾指针 rear 指向的位置可能在原队尾的后面,也可能在原队尾的前面
* 3.把 value 存储在队尾
*/
template <class T>
void CirQueue<T>::enqueue(T value){
if(isFull()){
cout<<"队满!"<<endl;
return;
}
rear = (rear+1) % MAX_LENGTH;
data[rear] = value;
}
/*
* 出队:
* 1.判断是否队空,若是则返回 -1,否则
* 2.队头指针 front 指向队头(队头指针,指向队头元素的前一个位置)
* 3.返回队头元素
* 注意:此时队头指针 front 指向原队头元素,即指向新队头元素的前一个位置
*/
template <class T>
T CirQueue<T>::dequeue(){
if(isEmpty()){
cout<<"队空!"<<endl;
return -1;
}
front = (front+1) % MAX_LENGTH;
return data[front];
}
/*
* 取队头元素:
* 1.判断是否队空,若是则返回 -1,否则
* 2.定义一个头指针 head ,其指向队头
* 3.返回队头元素
* 注意:不移动队头指针 front
*/
template <class T>
T CirQueue<T>::getHead(){
if(isEmpty()){
cout<<"队空!"<<endl;
return -1;
}
int head = (front+1) % MAX_LENGTH;
return data[head];
}
/*
* 遍历队的全部元素:
* 1.1.判断是否队空,若是则返回 ,否则
* 2.定义一个指针 p ,其指向队头
* 3.循环:当指针 p 未到达队尾时
* ①.输出指针 p 指向的元素
* ②.指针 p 指向下一个元素
* 4.输出队尾元素
*/
template <class T>
void CirQueue<T>::getAll(){
if(isEmpty()){
cout<<"队空!"<<endl;
return;
}
int p = (front+1) % MAX_LENGTH;
while(p != rear){
cout<<data[p]<<" ";
p = (p+1) % MAX_LENGTH;
}
cout<<data[p]<<endl;
}
/*
* 判断是否队空:
* 若队头指针 front 和队尾指针 rear 指向同一个位置则返回 true ,否则返回 false
*/
template <class T>
bool CirQueue<T>::isEmpty(){
return front == rear ? true : false;
}
/*
* 判断是否队满:
* 若队尾指针 rear 运算后和队头指针 front 指向同一个位置则返回 true ,否则返回 false
* 注意:因为为了方便判断,队有一个空间是空出来的,所以即便是队满但仍有一个位置“不存储元素”
*/
template <class T>
bool CirQueue<T>::isFull(){
return (rear+1) % MAX_LENGTH == front ? true : false;
}
四.测试

TestCirQueue.cpp

#include "CirQueue.h"
#include "CirQueue.cpp"
#include <iostream>
using namespace std;

int main(int argc, char **argv) {
int arr[] = {0,1,2,3,4};
cout<<"创建对象:0、1、2、3、4 依次入队"<<endl;
CirQueue<int> cirQueue(arr,5);
cout<<"遍历队内的元素:";
cirQueue.getAll();
cout<<"当前的队头元素:"<<cirQueue.getHead()<<endl;
cout<<endl;

while(!cirQueue.isEmpty()){
cout<<cirQueue.dequeue()<<"出队!"<<endl;
cout<<"遍历队内的元素:";
cirQueue.getAll();
cout<<"当前的队头元素:"<<cirQueue.getHead()<<endl;
cout<<endl;
}
cout<<endl;

for(int i = 0;!cirQueue.isFull();i++){
cout<<i<<"入队!"<<endl;
cirQueue.enqueue(i);
}
cout<<"遍历队内的元素:";
cirQueue.getAll();
cout<<"当前的队头元素:"<<cirQueue.getHead()<<endl;
cout<<endl;

return 0;
}
五.运行结果



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: