Logo

郎哥编程

队列结构及其应用

2026-07-28 110

在现实生活中,当人们去银行、行政大厅等企业和办事机构办理业务时,都需要从排队机领取排队号码,等待叫号。类似排队机这样的程序,其内部数据结构一般都会用到队列结构。

一、队列结构

队列结构和栈结构一样,都是特殊的线性表,但也有不同之处。栈结构只允许在表的一端插入和删除数据,而队列结构只允许在表的一端插入元素,而在另一端删除元素。允许插入元素的一端称为队尾,允许删除元素的一端称为队头。若给定一队列:

S = (a1,a2,……,an)

则称a1是队头元素,an是队尾元素,表中元素按an,……a2,a1的次序入队,出队的顺序是a1,a2,……,an。也就是说,队列结构的元素访问原则是先进先出,因此队列结构也称为先进先出的线性表,如图1所示。

图 1 队列结构

队列结构运算有入队、出队、访问队头元素、置队空四种基本运算。

(1)入队运算

该运算在长度为n的队列中,将元素置入队尾。若队列已满,返回出错信息。

(2)出队运算

该运算在长度为n的队列中,取出队首元素,并从队首删除该元素。若队列为空,返回出错信息。

(3)访问队首元素

该运算在长度为n的队列中,取出队首元素。若队列为空,返回出错信息。

(4)置队空

该运算将队列设置为空队列。

在程序中要实现队列结构,采用线性链表存储结构最为合适。每次的入队和出队运算无需移动队列元素,队列长度也可以动态变化。

下面的C语言代码实现了队列结构:

#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 10
typedef struct {
    int data[MAX_SIZE];
    int front;
    int rear;
} Queue;
void initializeQueue(Queue *q) {
    q->front = -1;
    q->rear = -1;
}
int isFull(Queue *q) {
    return (q->rear == MAX_SIZE - 1);
}
int isEmpty(Queue *q) {
    return (q->front == -1 || q->front > q->rear);
}
void enqueue(Queue *q, int value) {
    if (isFull(q)) {
        printf("Queue is full!\n");
        return;
    }
    if (isEmpty(q)) {
        q->front = 0;
    }
    q->rear++;
    q->data[q->rear] = value;
}
int dequeue(Queue *q) {
int value;
    if (isEmpty(q)) {
        printf("Queue is empty!\n");
        return -1;
    }
    value = q->data[q->front];
    q->front++;
    if (q->front > q->rear) {
        q->front = q->rear = -1;
    }
    return value;
}
int main() {
    Queue q;
    initializeQueue(&q);
    enqueue(&q, 1);
    enqueue(&q, 2);
    enqueue(&q, 3);
    printf("%d\n", dequeue(&q));  // 输出 1
    printf("%d\n", dequeue(&q));  // 输出 2
    return 0;
}

上述代码使用一个数组来存储队列的元素,并使用两个整数front和rear来跟踪队列的开头和结尾。enqueue函数用于向队列添加元素,dequeue函数用于从队列中移除元素,isFull和isEmpty函数来检查队列是否已满或为空。

注意:在实际应用中,可能还需要对队列进行更复杂的操作和优化,例如处理队列溢出、使用动态数组来动态调整队列的大小等。

二、数据结构设计

在银行业务办理自动叫号系统中,一般采用队列结构,因为它能够模拟顾客先来先服务的原则。

银行业务办理自动叫号系统需要满足以下功能:

顾客取号:当顾客到达银行时,系统分配一个唯一的号码给顾客。

号码显示:系统显示当前正在服务的顾客号码和下一个等待服务的顾客号码。

叫号:当柜台空闲时,系统自动叫下一个等待服务的顾客号码。

取消服务:如果顾客决定离开,可以取消其号码,并将其从队列中移除。

基于上述功能,定义的队列数据结构如下:

// 定义队列节点结构体
typedef struct QueueNode {
    int number; // 顾客号码
    struct QueueNode *next;
} QueueNode;
// 定义队列结构体
typedef struct Queue {
    QueueNode *front; // 队头指针
    QueueNode *rear; // 队尾指针
} Queue;

QueueNode为链表节点,number存储顾客号码,next指向链表的下一个节点。Queue为队列结构体,front指向队列的队头节点,rear指向队列的队尾节点。

三、取号和叫号

业务办理自动叫号有两个关键事件。一个事件是取号,当新的客户办理业务时,需要在排队机取号;一个事件是叫号,当业务办理窗口开始办理新业务时,需要叫号。取号事件是入队操作,叫号事件是出队操作。

// 入队操作:将新顾客号码添加到队尾
void enqueue(Queue *q, int number) {
    QueueNode *newNode = (QueueNode *)malloc(sizeof(QueueNode));
    newNode->number = number;
    newNode->next = NULL;
    if (isEmpty(q)) {
        q->front = q->rear = newNode;
    } else {
        q->rear->next = newNode;
        q->rear = newNode;
    }
}
// 出队操作:从队头取出下一个服务的顾客号码
int dequeue(Queue *q) {
    if (isEmpty(q)) {
        printf("Queue is empty.\n");
        return -1; // 返回错误码表示队列为空
    }
    QueueNode *temp = q->front;
    int number = temp->number;
    q->front = q->front->next;
    if (q->front == NULL) {
        q->rear = NULL; // 如果队列为空,则更新队尾指针
    }
    free(temp); // 释放已服务顾客的节点内存
    return number;
}

四、服务号码维护

可以使用两个整数变量来分别记录当前服务号码和下一个服务号码。当柜台空闲时,更新这两个变量以反映队列中的下一个顾客号码。

// 当前服务号码,初始化为-1表示无人服务
int currentServiceNumber = -1;
 // 下一个服务号码,初始化为-1表示无等待顾客
int nextServiceNumber = -1;
// 当柜台空闲时,调用此函数更新当前服务号码和下一个服务号码
void updateServiceNumbers(Queue *q) {
if (!isEmpty(q)) {
// 从队列中取出下一个服务的顾客号码
        nextServiceNumber = dequeue(q); 
// 更新当前服务号码为下一个服务号
        currentServiceNumber = nextServiceNumber; 码
} else {
// 如果没有等待顾客,则当前服务号码置为-1
        currentServiceNumber = -1; 
    }
}

评论区

登录 后发表评论
暂无评论