六、队列

队列(Queue)是一种操作受限的线性表,它只允许在表的一端进行元素插入(入队),而在另一端进行元素删除(出队)。允许插入的一端称为队尾(rear),允许删除的一端称为队头(front)

队列示意图
从上面队列示意图可以看出队列具有先进先出的特性,因此又称队列为先进先出表(First In First Out,FIFO表)

一、顺序队列:队列的顺序存储结构称为顺序队列。

队列的顺序存储也是利用一块连续的存储单元存放队列中的元素,实际上是一个受限的线性表。因为队头和队尾的位置是变化的,因此需要设置两个指针front和rear表示队头和队尾在表中的位置。顺序队列的类型可以定义如下:

#define QueueSize 100
typedef int DataType;
typedef struct 
{
    DataType data[QueueSize];
    int front, rear;
}SeqQueue;

基本运算

/*
    初始化队列
 */
void InitQueue(SeqQueue *q){
    q->front = 0;
    q->rear = 0;
}

/*
    入队
 */
void EnQueue(SeqQueue *q,int x){
    q->data[q->rear] = x;     //入队时将新元素插入队尾rear的位置
    q->rear = q->rear + 1;    //在将队尾加1
}

/*
    出队
 */
DataType DeQueue(SeqQueue *q){
    DataType x = q->data[q->front];   //出队时记录删除的元素
    q->front = q->front + 1;      //将队头加1
}

/*
    空判断
 */
int QueueEmpty(SeqQueue *q){
    //当头尾指针相等时队列为空
    return q->front == q->rear;
}

下面是一个入队出队的操作例子:
入队出队示意图
当队列处于上图 (f) 状态时,如果再插入新元素就会产生上溢,而出队时空出的一些存储单元无法使用。如果队列的存储空间定义的太大,会产生存储空间的浪费。

为了充分利用数组空间,克服上溢,可将数据空间想象为一个环状空间,如下图:
循环队列示意图

二、循环队列:环状数组表示的队列

在循环队列中进行入队、出队时,头尾指针仍然需要加1,只不过当头尾指针指向数组上界(QueueSize - 1) 时,如果按正常加1运算,数组会产生越界溢出,因此需要判断加1后是否超过数组上届,若是则是其指向数组下届0。用i表示Q.front或Q.rear。则这种加1运算可描述为:

if (i + 1 == QueueSize) 	// i 表示 Q.rear 或 Q.front
	i = 0;
else
	i = i + 1;

利用求余(%)运算符将上述操作简化

i = (i + 1) % QueueSize;

循环队列的顺序存储类型定义如下:

#define QueueSize 10
typedef int DataType;
typedef struct 
{
    DataType data[QueueSize];
    int front, rear;
}CirQueue;

基本方法如下:

/*
    初始化空队列
 */
void InitQueue(CirQueue *q){
    q->front = 0;
    q->rear = 0;
}

   

/*
    判队空
 */
int QueueEmpty(CirQueue *q){
    //当头尾指针相等时队列为空
    return q->front == q->rear;
}

/*
    判队满
    少用一个元素空间,约定入队前,测试尾指针在循环意义下加1是否等于头指针
    若相等则队列满,即尾指针rear指向单元格始终为空(相当于尾指针占用一个空间)
 */
int QueueFull(CirQueue *Q){
    return (Q->rear + 1) % QueueSize == Q->front;
}

/*
    入队列
 */
void EnQuque(CirQueue *Q,DataType x){
    printf("添加x = %d\n",x);   
    if (QueueFull(Q))
    {
        printf("队列已满\n");
    }
    else 
    {
        Q->data[Q->rear] = x;
        Q->rear = (Q->rear+1) % QueueSize;  //循环意义下的加1 
     
    }
}

/*
    获取队头元素
 */
DataType GetFront(CirQueue *Q){
    if (QueueEmpty(Q))
    {
        printf("队列为空\n");
        exit(0);
    }
    else{
        return Q->data[Q->front];
    }
}

/*
    出队列
 */
DataType DeQueue(CirQueue *Q){
    DataType x;
    if (QueueEmpty(Q))
    {
        printf("空队列\n");
        exit(0);
    }
    else
    {
        x = Q->data[Q->front];      //保存待删除的值
        Q->front = (Q->front + 1) % QueueSize;  //头指针加1
        return x;
    }
}

测试:

#include "stdio.h"
#include "CirQueue.c"

int main(){

    CirQueue C,*Q;
    Q = &C;
    InitQueue(Q);
  
    for (int i = 2; i < QueueSize; i++)
    {
        EnQueue(Q,i);
    }
    
    int front = GetFront(Q);
    printf("入队之后头指针=%d\n",front);
    DeQueue(Q);
    front = GetFront(Q);
    printf("出队之后头指针=%d\n",front);
    return 0;
}

运行结果:

入队之后头指针=2
出队之后头指针=3

三、链队列

队列的链式存储结构称为链队列

链队列是一种限制在表头删除和表尾插入操作的单链表。为了方便入队和出队操作,一个链队列就由头指针和尾指针唯一确定。链队列的类型定义如下:

typedef int DataType;
typedef struct qnode{
    DataType data;          //数据域
    struct qnode * next;    //下个结点指针域
}QueueNode;                 //链队列结点类型
typedef struct{
    QueueNode * front;      //队头指针
    QueueNode * rear;       //队尾指针
}LinkQueue;                 //链队列类型

链队列一般也是不带头指针的,为了简化边界条件处理,在队头结点前附加一个头结点,并设头指针指向此结点。链队列结构示意图如下:
链队列结构示意图l链队列的基本运算:

/*
    初始化队列
 */
void InitQueue(LinkQueue * Q){
    Q->front = (QueueNode *)malloc(sizeof(QueueNode));  //申请头结点
    Q->rear = Q->front;         //队尾结点也指向头结点
    Q->rear->next = NULL;
}

/*
    队判空
 */
int QueueEmpty(LinkQueue * Q){
    return Q->front == Q->rear; //头尾指针相等,队列为空
}

/*
    入队列
 */
void EnQueue(LinkQueue * Q, DataType x){
    QueueNode * p = (QueueNode *)malloc(sizeof(QueueNode)); //申请新结点
    p->data = x;
    p->next = NULL;
    Q->rear->next = p;  //*p链到原队尾结点之后
    Q->rear = p;        //队尾指针指向新的的队尾指针
}

/*
    获取头元素
 */
DataType GetFront(LinkQueue * Q){
    if (QueueEmpty(Q))
    {
        printf("空队列");
        exit(0);
    }
    else
    {
        return Q->front->next->data;    //返回队头元素
    }
}

/*
    出队列
 */
DataType DeQueue(LinkQueue * Q){
    QueueNode * p;
    if (QueueEmpty(Q))
    {
        printf("空队列");
        exit(0);
    }
    else
    {
        DataType x;
        p = Q->front;               //p指向头结点
        Q->front = Q->front->next;  //头指针指向原队头结点
        x = Q->front->data;
        free(p);                    //删除释放原头结点
        return x;      				//返回被删除结点值
    }
}

测试:

int main(){

    LinkQueue Q;
    InitQueue(&Q);

    EnQueue(&Q,10);
    int front = GetFront(&Q);
    printf("头元素=%d\n",front);
    EnQueue(&Q,100);
    DeQueue(&Q);
    front = GetFront(&Q);
    printf("头元素=%d\n",front);
    return 0;
}

运行结果:

头元素=10
头元素=100
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值