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

数据结构:队列的顺序存储结构(循环队列)

2015-02-10 14:56 260 查看
队列的定义:

队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表



队列的抽象数据类型:

ADT 队列(Queue)
Data
同线性表,元素具有相同的类型,相邻元素具有前驱和后继关系。
Operation
InitQueue(*Q):        初始化操作,建立一个空队列。
DestroyQueue(*Q):     若队列Q存在,则销毁它。
ClearQueue(*Q):       将队列清空。
QueueEmpty(Q):        判断队列是否为空。
GetHead(Q,*e):        若队列存在且非空,用e返回Q的队头元素。
EnQueue(*Q,e):        若队列Q存在,插入新元素e到队列Q中并成为队尾元素。
DeQueue(*Q,*e):       删除队列中的队头元素,并用e返回。
QueueLength(Q):       返回队列Q的元素的个数。

endADT


线性表有顺序存储和链式存储,栈是线性表,所以有这两种存储方式,有顺序栈和链栈。同样队列作为一种特殊的线性表,也存在这两种存储方式。先来看队列的顺序存储结构。

队列的顺序存储结构同线性表的顺序存储结构存在着同样的问题,队头出队列要后面的元素依次向前移动,时间复杂度为O(n)。





因为队列的每次删除元素都是从队头,所以每次删除都有大量的元素要移动,这样算法的效率很不好。于是改进一下,队头不一定要在数组的下标为0的位置。也就是说删除一个元素,仅需要把队头指针向后移动一次。





同时让front指针指向队头元素,rear指针指向队尾元素的下一个位置。这样当front等于rear时,队列就为空。

但是此时还有问题,会有假溢出的现象。





解决这种问题的办法就是循环队列。让上面的队尾指针rear指向数组的下标0位置。

队列的这种头尾相接的顺序存储结构称为循环队列

但是还存在一个问题:当队列空或者满的时候都是front==rear。那么这个要怎么判断呢?







(1)设置一个标志变量flag,当fron==rear且flag==0时,队列为空;当front==rear且flag==1时队列满。

(2)当队列为空时,条件就是front==rear,当队列满时修改条件,保留一个元素空间。也就是说队列满时,数组还有一个空闲单元。





这里假设队列的最大尺寸为QueueSize,所以队列满的条件是(rear+1)%QueueSize == front

队列的长度为(rear-front+QueueSize)%QueueSize

循环队列代码实现:

#include <iostream>
#include <stdlib.h>
using namespace std;
#define QueueSize 20

/********************************
*
*    循环队列的顺序存储结构代码
*
********************************/
typedef int QElemType;
typedef struct
{
QElemType data[QueueSize];
int front;
int rear;
}SqQueue;

/********************************
*
*    循环队列的操作函数实现代码
*
********************************/
/*初始化一个队列*/
bool InitQueue(SqQueue *Q)
{
Q->front = 0;
Q->rear = 0;

return true;
}
/*返回Q的元素的个数,也就是队列的当前长度*/
int QueueLength(SqQueue Q)
{
return (Q.rear-Q.front + QueueSize) % QueueSize;
}
/*若队列未满,插入元素e为Q的新队尾元素*/
bool EnQueue(SqQueue *Q, QElemType e)
{
if((Q->rear + 1) % QueueSize == Q->front)
{
return false;
}
cout << "EnQueue Item " << e << endl;
Q->data[Q->rear] = e;
Q->rear = (Q->rear + 1) % QueueSize;

return true;
}
/*若队列不空,删除Q中队头的元素,用e返回其值*/
bool DeQueue(SqQueue *Q, QElemType *e)
{
if(Q->front == Q->rear)
{
return false;
}

*e = Q->data[Q->front];
Q->front = (Q->front + 1) % QueueSize;
cout << "DeQueue Item " << *e << endl;

return true;
}
/*将队列清空*/
bool ClearQueue(SqQueue *Q)
{
Q->front = 0;
Q->rear = 0;
return true;
}
/*判断队列是否为空*/
bool IsEmptyQueue(SqQueue Q)
{
return Q.front == Q.rear;
}
/*返回队头元素*/
bool GetTop(SqQueue Q, QElemType *e)
{
if(IsEmptyQueue(Q))
{
return true;
}

*e = Q.data[Q.front];
cout << "Get Top Item:" << *e << endl;

return true;
}
/*遍历队列中的各个元素*/
bool QueueTraverse(SqQueue Q)
{
if(IsEmptyQueue(Q))
{
return false;
}
cout << "Queue Traverse ..." << endl;

int count =  (Q.rear - Q.front + QueueSize) % QueueSize;
for(int i = Q.front; i < Q.front + count; i++)
{
cout << Q.data[i] << ' ';
}
cout << endl;

return true;
}

void main(void)
{
SqQueue sq;
InitQueue(&sq);
for(int i = 0; i < 10; i++)
{
EnQueue(&sq, i);
}
QueueTraverse(sq);

if(!IsEmptyQueue(sq))
{
cout << "Queue Length: " << QueueLength(sq) << endl;
}

int result;
GetTop(sq, &result);
DeQueue(&sq, &result);
DeQueue(&sq, &result);

QueueTraverse(sq);

for(int i = 0; i < 11; i++)
{
EnQueue(&sq, i);
}
GetTop(sq, &result);
QueueTraverse(sq);

system("pause");
}


执行结果:





单是顺序存储,若不是循环队列,算法的时间性能是不高的,但循环队列又面临着数组可能溢出的问题,所以还是要研究一下不需要担心队列长度的链式存储结构。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: