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

队列的定义:


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

队列的抽象数据类型:

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)。

image

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

image

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

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

image

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

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

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

imageimage

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

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

image

这里假设队列的最大尺寸为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");
}

执行结果:

image

单是顺序存储,若不是循环队列,算法的时间性能是不高的,但循环队列又面临着数组可能溢出的问题,所以还是要研究一下不需要担心队列长度的链式存储结构。

posted @ 2015-02-10 14:56  stemon  阅读(1336)  评论(0编辑  收藏  举报