12. 队列

一、什么是队列

  队列(quene) 是一种具有操作约束的线性表,它只能在一端插入,在另一端删除。队列的基本操作是 入队(Enquene)和 出队(Dequene)。入队,它是在 队尾(表的末端)插入一个元素。出队,它是删除并返回 队头(表的开头)的元素。队列具有 先进先出(FIFO)的特性。

队列

ADT Queue
{
Data:
    队列Q∈Queue, 队头元素Item∈ElementType;
Operation:
    Queue CreateQueue(void);                        // 生成一个空的队列
    int IsEmpty(Queue PtrQ);                        // 队列是否为空
    void Enqueue(Queue PtrQ, ElementType Item);     // 入队
    ElementType Dequeue(Queue PtrQ);                // 出队
    ElementType Peek(Queue PtrQ);                   // 获取队头元素
} ADT Queue;

二、队列的顺序存储实现

2.1、队列的顺序存储

顺序队列

#define MAX_SIZE 10

typedef int ElementType;

typedef struct QNode
{
    int Data[MAX_SIZE];
    int Front;
    int Rear;
}QNode, * Queue;

这里,队列,我们只使用 MAX_SIZE - 1 个元素,如果使用 MAX_SIZE 元素,则 Front == Rear 是无法判断队列是空的还是满的。如果使用 MAX_SIZE - 1 个元素,则 Front == Rear 是队列是空的,(Rear + 1) % MAX_SIZE == Front 时,队列是满的。

2.2、创建空的队列

/**
 * @brief 创建一个空的队列
 * 
 * @return Queue 创建的空的队列
 */
Queue CreateQueue(void)
{
    Queue PtrQ = (Queue)malloc(sizeof(QNode));
    memset(PtrQ->Data, 0, sizeof(ElementType) * MAX_SIZE);
    PtrQ->Front = PtrQ->Rear = 0;
    return PtrQ;
}

2.3、判断队列是否为空

/**
 * @brief 判断队列是否为空
 * 
 * @param PtrQ 队列
 * @return int 0: 队列非空; 1: 队列为空
 */
int IsEmpty(Queue PtrQ)
{
    return PtrQ->Front == PtrQ->Rear;
}

2.4、入队

/**
 * @brief 入队
 * 
 * @param PtrQ 队列
 * @param Item 要入队的元素
 */
void Enqueue(Queue PtrQ, ElementType Item)
{
    if ((PtrQ->Rear + 1) % MAX_SIZE == PtrQ->Front)
    {
        printf("队列已满!\n");
        return;
    }
    PtrQ->Rear = (PtrQ->Rear + 1) % MAX_SIZE;
    PtrQ->Data[PtrQ->Rear] = Item;
}

2.5、出队

/**
 * @brief 出队
 * 
 * @param PtrQ 队列
 * @return ElementType 要出队的元素
 */
ElementType Dequeue(Queue PtrQ)
{
    if (PtrQ->Front == PtrQ->Rear)
    {
        printf("队列为空!\n");
        return NULL;
    }
    PtrQ->Front = (PtrQ->Front + 1) % MAX_SIZE;
    return PtrQ->Data[PtrQ->Front];
}

2.6、获取队头元素

/**
 * @brief 获取队头元素
 * 
 * @param PtrQ 队列
 * @return ElementType 队头元素
 */
ElementType Peek(Queue PtrQ)
{
    if (PtrQ->Front == PtrQ->Rear)
    {
        printf("队列为空!\n");
        return NULL;
    }
    return PtrQ->Data[PtrQ->Front + 1];
}

2.7、遍历队列

/**
 * @brief 遍历队列
 * 
 * @param PtrQ 队列
 */
void PrintQueue(Queue PtrQ)
{
    if (PtrQ->Front == PtrQ->Rear)
    {
        return;
    }

    int i = PtrQ->Front + 1;
    while (i != PtrQ->Rear)
    {
        printf("%d ", PtrQ->Data[i]);
        i = (i + 1) % MAX_SIZE;
    }
    printf("%d\n", PtrQ->Data[PtrQ->Rear]);
}

三、队列的链式存储实现

3.1、队列的链式存储

  队列也可以使用单链表进行链式存储,此时队头指针应该指向链表头,队尾指针指向链表尾。

链队列

typedef int ElementType;

typedef struct QNode
{
    ElementType Data;
    struct QNode *Next;
} QNode, Queue;

typedef struct PQueue
{
    QNode * Front;
    QNode * Rear;
}PQNode, * PQueue;

3.2、创建空的队列

/**
 * @brief 创建一个空的队列
 * 
 * @return Queue 创建的空的队列
 */
PQueue CreateQueue(void)
{
    PQueue PtrQ = (PQueue)malloc(sizeof(PQNode));
    PtrQ->Front = PtrQ->Rear = NULL;
    return PtrQ;
}

3.3、判断队列是否为空

/**
 * @brief 判断队列是否为空
 * 
 * @param PtrQ 队列
 * @return int 0: 队列非空; 1: 队列为空
 */
int IsEmpty(PQueue PtrQ)
{
    return (PtrQ == NULL || PtrQ->Front == NULL);
}

3.4、入队

/**
 * @brief 入队
 * 
 * @param PtrQ 队列
 * @param Item 要入队的元素
 */
void Enqueue(PQueue PtrQ, ElementType Item)
{
    QNode * RearNode = malloc(sizeof(QNode));
    RearNode->Data = Item;
    RearNode->Next = NULL;

    if (PtrQ->Front == NULL)                                                    // 若队列为空
    {
        PtrQ->Front = RearNode;                                                 // 队列头指针指向新节点
        PtrQ->Rear = RearNode;                                                  // 队列尾指针指向新节点
    }
    else
    {
        PtrQ->Rear->Next = RearNode;                                            // 原队列尾指针指向新节点
        PtrQ->Rear = RearNode;                                                  // 队列尾指针指向新节点
    }
}

3.5、出队

/**
 * @brief 出队
 * 
 * @param PtrQ 队列
 * @return ElementType 要出队的元素
 */
ElementType Dequeue(PQueue PtrQ)
{
    QNode * FrontNode = NULL;
    ElementType FrontElement = 0;

    if (PtrQ->Front == NULL)
    {
        printf("队列为空!\n");
        return NULL;
    }

    FrontNode = PtrQ->Front;
    if (PtrQ->Front == PtrQ->Rear)                                              // 若队列只有一个元素
    {
        PtrQ->Front = PtrQ->Rear = NULL;                                        // 删除后队列置为空
    }
    else
    {
        PtrQ->Front = PtrQ->Front->Next;
    }
  
    FrontElement = FrontNode->Data;
    free(FrontNode);

    return FrontElement;
}

3.6、获取队头元素

/**
 * @brief 获取队头元素
 * 
 * @param PtrQ 队列
 * @return ElementType 队头元素
 */
ElementType Peek(PQueue PtrQ)
{
    if (PtrQ->Front == NULL)
    {
        printf("队列为空!\n");
        return NULL;
    }

    return PtrQ->Front->Data;
}

3.7、遍历队列

void PrintQueue(PQueue PtrQ)
{
    QNode * Node = PtrQ->Front;

    if (PtrQ->Front == NULL)
    {
        return;
    }
  
    while (Node != PtrQ->Rear)
    {
        printf("%d ", Node->Data);
        Node = Node->Next;
    }
    printf("%d\n", Node->Data);
}
posted @ 2023-07-07 18:40  星光樱梦  阅读(12)  评论(0编辑  收藏  举报