《数据结构与算法分析——C语言描述》ADT实现(NO.02) : 队列(Queue)

第三个结构——队列(Queue)

队列与上次的栈相反,是一种先进先出(FIFO)的线性表。写入时只暴露尾部,读取时只暴露头部。

本次只实现了数组形式的队列。原因是链表形式的队列极为简单,只需要实现简单的删除首结点和尾部插入两种操作,在此便不再具体实现。

而对于数组形式的队列,内存单元固定,又不具备像栈一样一端固定的特性,为使数组中的空间可被重复使用,需使得队列的头部随着Dequeue的操作而移动。如果每次Dequeue都将整个队列的内容前移一个单元,将是一个O(n)复杂度的操作,对于删除操作而言开销过大。因此,我们不对元素本身进行移动,而是通过两个标识来指示队列的头(Front)和尾(Rear),随着Dequeue操作,向后移动Front。这样一来,前面的空间空余出来,当尾部达到数组尽头时,回过头使用数组头部的空间。也就是形成一个循环数组。

此时,我们通过Front和Rear之间的大小关系即可得知队列的长度,是否为满,是否为空。

特别需要注意的是,一旦形成循环,我们设想一下当Front确定之后,共可以形成多少种不同长度的队列?

显然,Rear可以取的值的数目与数组的长度(设为m)相等。也就是说,相应的队列长度可能为0, 1, 2, 3, ..., m-1. 这就是问题所在,一个长度为m的数组,形成循环结构后所能表示的队列的最大长度是m-1.

我们不妨思考一下原因。事实上,假如我们使用长度为m的数组表示最大长度为m的队列,如果:

  (i)Front指示第一个元素的位置,Rear指示最后一个元素的位置。那么我们无法表示空队列的情况。

  (ii)Front指示首元素之前的位置(类似于链表中的头结点,不保存实际数据),当Rear与Front重合时表示空队列,那么将无法表示长度为m的队列。因为不论Front处于什么位置、不论队列长度是多少,总有一个位置是不保存数据的。Rear指示尾元素之后的位置与上述情况相同,故不再单列为(iii)。

因此,要创建最大长度为n的队列,需要申请(n+1) * sizeof( ElementType ) 的数组空间。这般说下来看似废话,好像很容易可以想到,但事实上对队列不甚熟悉时,编写队列的实现很有可能忽略这一点。

至于具体将空单元放在首元素之前(Front指示)还是尾元素之后(Rear指示),则没有太大的影响。下面的实现中,笔者采用了前一种方案。

下面给出代码

// Queue.h

#include <stdio.h>
#include <stdlib.h>

struct QueueRecord;
typedef struct QueueRecord *Queue;

int IsEmpty(Queue Q);
int IsFull(Queue Q);
Queue CreateQueue(int MaxElements);
void DisposeQueue(Queue Q);
void MakeEmpty(Queue Q);
void Enqueue(ElementType X, Queue Q);
ElementType Front(Queue Q);
void Dequeue(Queue Q);
ElementType FrontAndDequeue(Queue Q);

  

// Queue.c

#include "Queue.h"

struct QueueRecord{
    int Capacity;
    int Front;
    int Rear;
    int Size;
    ElementType *Array;
};


int IsEmpty(Queue Q)
{
    return Q->Size == 0;
}

int IsFull(Queue Q)
{
    return Q->Size == Q->Capacity;
}

Queue CreateQueue(int MaxElements)
{
    Queue ret;
    if((ret = (Queue)malloc(sizeof(struct QueueRecord))) == NULL)
    {
        printf("Error! Out of memory! \n");
        return NULL;
    }
    if((ret->Array = (ElementType*)malloc(sizeof(ElementType) * (1 + MaxElements))) == NULL)
    {
        printf("Error! Out of memory! \n");
        free(ret);
        return NULL;
    }
    ret->Capacity = MaxElements;
    ret->Size = 0;
    ret->Front = ret->Rear = 0;
    return ret;
}

void DisposeQueue(Queue Q)
{
    if(Q)
    {
        free(Q->Array);
        free(Q);
    }
}

void MakeEmpty(Queue Q)
{
    Q->Rear = Q->Front;
    Q->Size = 0;
}

void Enqueue(ElementType X, Queue Q)
{
    int t;
    if(IsFull(Q))
    {
        printf("Error! The queue is full! \n");
        return;
    }
    t = (Q->Rear + 1) % (Q->Capacity + 1);
    Q->Array[t] = X;
    Q->Rear = t;
    Q->Size += 1;
}

ElementType Front(Queue Q)
{
    if (IsEmpty(Q))
    {
        printf("Error! The queue is empty! \n");
        return 0;
    }
    return (Q->Array)[Q->Front];
}

void Dequeue(Queue Q)
{
    if (IsEmpty(Q))
    {
        printf("Error! The queue is empty! \n");
        return;
    }
    Q->Front = (Q->Front + 1) % (Q->Capacity + 1);
    Q->Size -= 1;
}

ElementType FrontAndDequeue(Queue Q)
{
    ElementType ret;
    if (IsEmpty(Q))
    {
        printf("Error! The queue is empty! \n");
        return 0;
    }
    ret = (Q->Array)[Q->Front];
    Q->Front = (Q->Front + 1) % (Q->Capacity + 1);
    Q->Size -= 1;
    return ret;
}

  

猜你喜欢

转载自www.cnblogs.com/DrChuan/p/11280034.html