数据结构 - 顺序队列的实行(C语言)

数据结构-顺序队列的实现

1 顺序队列的定义

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

队列的顺序储存结构:用数组存储队列,为了避免当只有一个元素时,队头和队尾重合使得处理变得麻烦,所以引入两个指针:front指针指向队头元素,rear指针指向队尾元素的下一个位置,当front=rear时,为空队列,结构如下图所示。

假设是长度为5的数组,初始状态,空队列如下图左所示,front与 rear指针均指向下标为0的位置。然后入队a1、a2、a3、a4,front指针依然指向下标为0位置,而rear指针指向下标为4的位置,如下图右所示。

img

出队a1、a2,则front指针指向下标为2的位置,rear不变,如下图左所示,再入队a5,此时front指针不变,rear指针移动到数组之外。嗯?数组之外, 那将是哪里?如下图右所示。

img

问题还不止于此。假设这个队列的总个数不超过5个,但目前如果接着入队的话,因数组末尾元素已经占用,再向后加,就会产生数组越界的错误,可实际上,我们的队列在下标为0和1的地方还是空闲的。我们把这种现象叫做“假溢出"。

2 循环队列定义

所以解决"假溢出"的办法就是后面满了,就再从头开始,也就是头尾相接的循环。我们把队列的这种头尾相接的顺序存储结构称为循环队列。

刚才的例子继续,上图的rear可以改为指向下标为0的位置,这样就不会造成指针指向不明的问题了,如下图所示。

img

接着入队a6,将它放置于下标为0处,rear指针指向下标为1处,如下图左所示。若再入队a7,则rear指针就与front指针重合,同时指向下标为2的位置,如下图右所示。

img

  • 此时问题又出来了,我们刚才说,空队列时,front等于rear,现在当队列满
    时,也是front等于rear,那么如何判断此时的队列究竟是空还是满呢?
  • 办法一是设置一个标志变量flag, 当front == rear,且flag = 0时为队列空,
    当front== rear,且flag= 1 时为队列满。
  • 办法二是当队列空时,条件就是front = rear,当队列满时,我们修改其条件,保留一个元素空间。也就是说,队列满时,数组中还有一个空闲单元。
    例如下图所示,我们就认为此队列已经满了,
    也就是说,我们不允许上图右的情况出现。

img

我们重点来讨论第二种方法,由于rear可能比front大,也可能比front小,所以尽管它们只相差一个位置时就是满的情况,但也可能是相差整整一圈。 所以若队列的最大尺寸为QueueSize,那么队列满的条件是(rear+1) % QueueSize == front

通用的计算队列长度公式为:(rear - front + QueueSize) % QueueSize

注意:front指针和rear指针后移不能直接使用++,而要使用%:Q->front = (Q->front + 1) % MAXSIZE,因为到达数组尾后需要移动到数组开头。


3 循环队列完整实现

#include "stdafx.h" 
#include "stdlib.h"   

#define TRUE 1
#define FALSE 0
#define MAXSIZE 5 /* 存储空间初始分配量 */

typedef int Status;
typedef int QElemType; /* QElemType类型根据实际情况而定,这里假设为int */

/* 循环队列的顺序存储结构 */
typedef struct
{
    QElemType data[MAXSIZE];
    int front;      /* 头指针 */
    int rear;       /* 尾指针,若队列不空,指向队列尾元素的下一个位置 */
}SqQueue;

Status visit(QElemType c)
{
    printf("%d ", c);
    return TRUE;
}

/* 初始化一个空队列Q */
Status InitQueue(SqQueue *Q)
{
    Q->front = 0;
    Q->rear = 0;
    return  TRUE;
}

/* 将Q清为空队列 */
Status ClearQueue(SqQueue *Q)
{
    Q->front = Q->rear = 0;
    return TRUE;
}

/* 若队列Q为空队列,则返回TRUE,否则返回FALSE */
Status QueueEmpty(SqQueue Q)
{
    if (Q.front == Q.rear) /* 队列空的标志 */
        return TRUE;
    else
        return FALSE;
}

/* 返回Q的元素个数,也就是队列的当前长度 */
int QueueLength(SqQueue Q)
{
    return  (Q.rear - Q.front + MAXSIZE) % MAXSIZE;
}

/* 若队列不空,则用e返回Q的队头元素,并返回TRUE,否则返回FALSE */
Status GetHead(SqQueue Q, QElemType *e)
{
    if (Q.front == Q.rear) /* 队列空 */
        return FALSE;
    *e = Q.data[Q.front];
    return TRUE;
}

/* 若队列未满,则插入元素e为Q新的队尾元素 */
Status EnQueue(SqQueue *Q, QElemType e)
{
    if ((Q->rear + 1) % MAXSIZE == Q->front)    /* 队列满的判断 */
        return FALSE;
    Q->data[Q->rear] = e;           /* 将元素e赋值给队尾 */
    Q->rear = (Q->rear + 1) % MAXSIZE;/* rear指针向后移一位置, */
    /* 若到最后则转到数组头部 */
    return  TRUE;
}

/* 若队列不空,则删除Q中队头元素,用e返回其值 */
Status DeQueue(SqQueue *Q, QElemType *e)
{
    if (Q->front == Q->rear)            /* 队列空的判断 */
        return FALSE;
    *e = Q->data[Q->front];             /* 将队头元素赋值给e */
    Q->front = (Q->front + 1) % MAXSIZE;    /* front指针向后移一位置, */
    /* 若到最后则转到数组头部 */
    return  TRUE;
}

/* 从队头到队尾依次对队列Q中每个元素输出 */
Status QueueTraverse(SqQueue Q)
{
    int i;
    i = Q.front;
    while ((i + Q.front) != Q.rear)
    {
        visit(Q.data[i]);
        i = (i + 1) % MAXSIZE;
    }
    printf("\n");
    return TRUE;
}

int main()
{
    Status j;
    int i = 0, l;
    QElemType d;
    SqQueue Q;
    InitQueue(&Q);
    printf("初始化队列后,队列空否?%u(1:空 0:否)\n", QueueEmpty(Q));

    printf("请输入整型队列元素(不超过%d个),-1为提前结束符: ", MAXSIZE - 1);
    do
    {
        /* scanf("%d",&d); */
        d = i + 100;
        if (d == -1)
            break;
        i++;
        EnQueue(&Q, d);
    } while (i<MAXSIZE - 1);

    printf("队列长度为: %d\n", QueueLength(Q));
    printf("现在队列空否?%u(1:空 0:否)\n", QueueEmpty(Q));
    printf("连续%d次由队头删除元素,队尾插入元素:\n", MAXSIZE);
    for (l = 1; l <= MAXSIZE; l++)
    {
        DeQueue(&Q, &d);
        printf("删除的元素是%d,插入的元素:%d \n", d, l + 1000);
        /* scanf("%d",&d); */
        d = l + 1000;
        EnQueue(&Q, d);
    }
    l = QueueLength(Q);

    printf("现在队列中的元素为: \n");
    QueueTraverse(Q);
    printf("共向队尾插入了%d个元素\n", i + MAXSIZE);
    if (l - 2>0)
        printf("现在由队头删除%d个元素:\n", l - 2);
    while (QueueLength(Q)>2)
    {
        DeQueue(&Q, &d);
        printf("删除的元素值为%d\n", d);
    }

    j = GetHead(Q, &d);
    if (j)
        printf("现在队头元素为: %d\n", d);
    ClearQueue(&Q);
    printf("清空队列后, 队列空否?%u(1:空 0:否)\n", QueueEmpty(Q));
    return 0;
}

/*
输出结果:

初始化队列后,队列空否?1(1:空 0:否)
请输入整型队列元素(不超过4个),-1为提前结束符: 队列长度为: 4
现在队列空否?0(1:空 0:否)
连续5次由队头删除元素,队尾插入元素:
删除的元素是100,插入的元素:1001
删除的元素是101,插入的元素:1002
删除的元素是102,插入的元素:1003
删除的元素是103,插入的元素:1004
删除的元素是1001,插入的元素:1005
现在队列中的元素为:
1002 1003 1004 1005
共向队尾插入了9个元素
现在由队头删除2个元素:
删除的元素值为1002
删除的元素值为1003
现在队头元素为: 1004
清空队列后, 队列空否?1(1:空 0:否)
*/

从这一段讲解,大家应该发现,单是顺序存储,若不是循环队列,算法的时间性能是不高的,但循环队列又面临着数组可能会溢出的问题。

转载于:https://www.cnblogs.com/linuxAndMcu/p/10327803.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值