数据结构之栈与队列

一,栈和队列的区别

1、核心定义与特性

特性 栈(Stack) 队列(Queue)
定义 仅允许在栈顶 (表尾)进行插入和删除的线性表,遵循 后进先出(LIFO) 允许在队尾 插入、队头 删除的线性表,遵循 先进先出(FIFO)
操作限制 插入(入栈 / Push)和删除(出栈 / Pop)只能在栈顶进行。 插入(入队 / Enqueue)在队尾,删除(出队 / Dequeue)在队头。
典型场景 适合 "后处理先完成" 的场景,如撤销操作、函数调用栈。 适合 "先处理先完成" 的场景,如任务排队、消息缓冲。

2、操作对比

操作 队列
插入位置 栈顶(表尾),新元素成为新栈顶。 队尾,新元素追加到队列末尾。
删除位置 栈顶,删除最后插入的元素(LIFO)。 队头,删除最早插入的元素(FIFO)。
核心操作 Push(入栈)、Pop(出栈)、GetTop(取栈顶)。 Enqueue(入队)、Dequeue(出队)、GetHead(取队头)。
时间复杂度 均为 O(1)(仅修改栈顶指针)。 均为 O(1)(仅修改队头 / 队尾指针)。

3、数据结构实现

3.1. 存储方式

    • 顺序栈 :用数组实现,通过top指针指向栈顶(下标),栈满时top = MAXSIZE-1,栈空时top = -1
      • 优化:两栈共享空间(利用数组两端作为栈底,减少空间浪费)。
    • 链栈:用链表实现,头指针作为栈顶,插入 / 删除在头部进行(头插法),无栈满限制(仅受内存限制)。
  • 队列

    • 顺序队列(循环队列) :用数组实现,通过front(队头)和rear(队尾下一个位置)指针管理,通过取模运算实现循环,解决假溢出问题。
      • 队满条件:(rear + 1) % MAXSIZE == front(牺牲一个单元区分空和满)。
    • 链队列:用链表实现,带头尾指针,入队在队尾(尾插法),出队在队头(删除头结点的后继)。

3.2. 结构示意图

  • plaintext

    复制代码
    栈顶 → [top] 新元素(入栈)/ 旧元素(出栈)  
    栈底 → [bottom](固定端)  
  • 队列

    plaintext

    复制代码
    队头 → [front] 旧元素(出队) ← 队尾 [rear] 新元素(入队)  

4、典型应用场景

场景 栈的应用 队列的应用
函数调用 存储函数调用的参数、返回地址(系统栈自动管理)。 无直接应用,函数调用本质是栈结构。
表达式处理 后缀表达式求值、中缀转后缀(处理运算符优先级)。 无相关应用。
括号匹配 检测括号是否正确嵌套(如{[()]})。 无相关应用。
任务调度 无直接应用,适合 "逆序处理"(如撤销操作)。 打印机任务队列、操作系统进程调度(FIFO)。
数据遍历 深度优先搜索(DFS,递归本质是栈)。 广度优先搜索(BFS,逐层遍历图或树)。
缓冲区管理 无直接应用。 输入输出缓冲区(如键盘输入、网络数据接收)。

5、核心区别总结

  1. 操作顺序

    • 栈:后进先出(LIFO)------ 最后插入的元素最先被删除(如弹夹装弹,最后装入的子弹最先射出)。
    • 队列:先进先出(FIFO)------ 最先插入的元素最先被删除(如排队买票,先来先服务)。
  2. 适用场景

    • 栈适合处理 "逆序依赖" 问题(如函数递归、表达式求值中的括号匹配)。
    • 队列适合处理 "顺序依赖" 问题(如任务排队、消息缓冲、逐层遍历)。
  3. 数据结构差异

    • 栈的操作集中在一端,实现简单;队列需要两端操作,顺序存储需处理循环逻辑(避免假溢出)。

6、一句话区分

  • :像 "叠盘子",最后放上去的最先被拿走(LIFO)。
  • 队列:像 "排队候车",最先排队的人最先上车(FIFO)

二、栈与递归的关系:递归的本质是栈的应用

1. 递归的定义与栈的底层原理

递归是指函数直接或间接调用自身的过程,其核心依赖系统栈(调用栈)来保存每次调用的状态(参数、局部变量、返回地址),确保函数能正确返回并恢复上下文。

  • 压栈过程:每次递归调用时,系统将当前函数的参数、局部变量、返回地址压入栈,形成 "调用帧"。
  • 出栈过程:当递归终止条件满足时,从栈顶取出最近的调用帧,恢复上下文并继续执行。
示例:斐波那契数列递归实现
cpp 复制代码
int fib(int n) {
    if (n <= 1) return n; // 终止条件
    return fib(n-1) + fib(n-2); // 递归调用
}
  • 栈行为 :计算fib(5)时,系统栈会依次压入fib(5)fib(4)fib(3)fib(2)fib(1)(终止),然后逐层出栈计算返回值,最终得到结果。
  • 缺点 :递归深度过大会导致栈溢出(如fib(100)),且重复计算效率低(可通过记忆化优化)。

2. 递归 vs 手动栈实现

特性 递归(系统栈) 手动栈(用户实现)
管理方式 系统自动管理调用帧,无需手动操作。 需要手动压栈、出栈(如表达式求值)。
适用场景 适合树形 / 分治问题(如 DFS、回溯),代码简洁。 适合显式需要 LIFO 的场景(如表达式转换)。
风险 可能栈溢出(递归深度超过系统栈容量)。 需手动处理栈满 / 栈空逻辑。

三、栈在四则运算中的应用:中缀表达式→后缀表达式→求值

1. 表达式的三种表示形式

  • 中缀表达式 :操作符在操作数中间(如 3 + 4 * 2 - 1),符合人类习惯,但计算机处理需要考虑优先级和括号。
  • 后缀表达式(逆波兰式) :操作符在操作数之后(如 3 4 2 * + 1 -),无需括号,计算机可直接求值。
  • 前缀表达式(波兰式) :操作符在操作数之前(如 - + 3 * 4 2 1),较少使用。

2. 中缀转后缀表达式(核心:栈处理运算符优先级)

规则
  1. 初始化空栈,用于存储运算符。
  2. 遍历中缀表达式
    • 若为操作数:直接加入后缀表达式。
    • 若为左括号 (:压栈。
    • 若为右括号 ):弹出栈中运算符直到遇到左括号(左括号不加入后缀表达式)。
    • 若为运算符
      • 若栈空或栈顶为左括号,直接压栈。
      • 否则,若当前运算符优先级 栈顶运算符优先级,弹出栈顶运算符加入后缀表达式,直到条件不满足,再压栈当前运算符。
  3. 遍历结束后:弹出栈中剩余运算符加入后缀表达式。
优先级定义(从高到低):

* / % > + - > ( )(括号仅用于界定优先级,本身无优先级)。

示例:中缀表达式 3 + 4 * 2 - 1 转后缀
  • 遍历过程:
    1. 3:操作数,直接输出 → 3
    2. +:栈空,压栈 → 栈:+
    3. 4:操作数,输出 → 3 4
    4. *:优先级高于栈顶+,压栈 → 栈:+ *
    5. 2:操作数,输出 → 3 4 2
    6. -:优先级低于栈顶*,弹出* → 输出3 4 2 *,栈:+;再比较-+优先级相等,弹出+ → 输出3 4 2 * +,栈空,压栈-
    7. 1:操作数,输出 → 3 4 2 * + 1
    8. 遍历结束,弹出栈中- → 最终后缀表达式:3 4 2 * + 1 -

3. 后缀表达式求值(核心:栈处理操作数)

规则
  1. 初始化空栈,用于存储操作数。
  2. 遍历后缀表达式
    • 若为操作数:压栈。
    • 若为运算符:弹出栈顶两个操作数(先弹出的是右操作数,后弹出的是左操作数),计算结果后压栈。
  3. 遍历结束后:栈顶即为最终结果。
示例:后缀表达式 3 4 2 * + 1 - 求值
  • 遍历过程:
    1. 3:压栈 → 栈:[3]
    2. 4:压栈 → 栈:[3, 4]
    3. 2:压栈 → 栈:[3, 4, 2]
    4. *:弹出24,计算4×2=6,压栈 → 栈:[3, 6]
    5. +:弹出63,计算3+6=9,压栈 → 栈:[9]
    6. 1:压栈 → 栈:[9, 1]
    7. -:弹出19,计算9-1=8,压栈 → 最终结果:8

五,栈的示例代码

① 栈的基本操作

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define MAX_SIZE 100

// 定义栈结构体
typedef struct {
    int data[MAX_SIZE];
    int top;
} Stack;

// 初始化栈
void initStack(Stack* s) 
{
    s->top = -1;
}

// 判断栈是否为空
bool isEmpty(Stack* s) 
{
    return s->top == -1;
}

// 判断栈是否已满
bool isFull(Stack* s)
{
    return s->top == MAX_SIZE - 1;
}

// 入栈操作
bool push(Stack* s, int value) 
{
    if (isFull(s)) //如果栈未满,先将 top 的值加 1(++(s->top)),然后将 value 存入 data 数组中 top 所指向的位置
    {
        printf("栈已满,无法入栈!\n");
        return false;
    }
    s->data[++(s->top)] = value;
    return true;
}

// 出栈操作
bool pop(Stack* s, int* value)
{
    if (isEmpty(s)) 
    {
        printf("栈为空,无法出栈!\n");
        return false;
    }
    *value = s->data[(s->top)--];//*value = s->data[(s->top)--];:如果栈不为空,先将 top 所指向的元素赋值给 *value(即 value 所指向的变量),然后将 top 的值减 1((s->top)--)
    return true;
}

// 获取栈顶元素
bool peek(Stack* s, int* value) {
    if (isEmpty(s)) {
        printf("栈为空,无栈顶元素!\n");
        return false;
    }
    *value = s->data[s->top];
    return true;
}

int main() {
    Stack s;
    initStack(&s);

    push(&s, 10);
    push(&s, 20);
    push(&s, 30);

    int value;
    if (peek(&s, &value)) {
        printf("栈顶元素是: %d\n", value);
    }

    if (pop(&s, &value)) {
        printf("出栈元素是: %d\n", value);
    }

    return 0;
}

② 栈的应用实例

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

#define MAX_SIZE 100

// 定义栈结构体
typedef struct {
    char data[MAX_SIZE];
    int top;
} Stack;

// 初始化栈
void initStack(Stack *s) {
    s->top = -1;
}

// 判断栈是否为空
bool isEmpty(Stack *s) {
    return s->top == -1;
}

// 判断栈是否已满
bool isFull(Stack *s) {
    return s->top == MAX_SIZE - 1;
}

// 入栈操作
bool push(Stack *s, char value) {
    if (isFull(s)) {
        printf("栈已满,无法入栈!\n");
        return false;
    }
    s->data[++(s->top)] = value;
    return true;
}

// 出栈操作
bool pop(Stack *s, char *value) {
    if (isEmpty(s)) {
        printf("栈为空,无法出栈!\n");
        return false;
    }
    *value = s->data[(s->top)--];
    return true;
}

// 获取栈顶元素
bool peek(Stack *s, char *value) {
    if (isEmpty(s)) {
        printf("栈为空,无栈顶元素!\n");
        return false;
    }
    *value = s->data[s->top];
    return true;
}

// 判断左右括号是否匹配
bool is_matching_pair(char left, char right) {
    if (left == '(' && right == ')') return true;
    if (left == '[' && right == ']') return true;
    if (left == '{' && right == '}') return true;
    return false;
}

// 检查表达式括号是否匹配
bool is_balanced(const char *expression) {
    Stack s;
    initStack(&s);
    for (int i = 0; expression[i] != '\0'; i++) {
        if (expression[i] == '(' || expression[i] == '[' || expression[i] == '{') {
            // 如果是左括号,入栈
            push(&s, expression[i]);
        } else if (expression[i] == ')' || expression[i] == ']' || expression[i] == '}') {
            if (isEmpty(&s)) {
                // 如果栈为空,说明没有匹配的左括号
                return false;
            }
            char top;
            pop(&s, &top);
            if (!is_matching_pair(top, expression[i])) {
                // 如果弹出的左括号和当前右括号不匹配
                return false;
            }
        }
    }
    // 最后检查栈是否为空,如果为空则括号完全匹配
    return isEmpty(&s);
}

int main() {
    const char *expressions[] = {"{[()]}", "{[(])}", "((()", "())"};
    int num_expressions = sizeof(expressions) / sizeof(expressions[0]);
    for (int i = 0; i < num_expressions; i++) {
        printf("表达式 %s 是否括号匹配: %s\n", expressions[i], is_balanced(expressions[i]) ? "是" : "否");
    }
    return 0;
}

③使用栈实现汉诺塔问题

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

#define MAX_SIZE 100

/*汉诺塔问题的传统解法是递归,
这里使用栈来模拟递归调用的过程。栈可以保存每一步的状态,
包括盘子数量、柱子信息等,通过不断地入栈和出栈操作,实现盘子的移动。*/
// 定义栈结构体
typedef struct {
    int data[MAX_SIZE];
    int top;
} Stack;

// 初始化栈
void initStack(Stack* s) {
    s->top = -1;
}

// 判断栈是否为空
int isEmpty(Stack* s) {
    return s->top == -1;
}

// 判断栈是否已满
int isFull(Stack* s) {
    return s->top == MAX_SIZE - 1;
}

// 入栈操作
void push(Stack* s, int value) {
    if (isFull(s)) {
        printf("栈已满,无法入栈!\n");
        return;
    }
    s->data[++(s->top)] = value;
}

// 出栈操作
int pop(Stack* s) {
    if (isEmpty(s)) {
        printf("栈为空,无法出栈!\n");
        return -1;
    }
    return s->data[(s->top)--];
}

// 获取栈顶元素
int peek(Stack* s) {
    if (isEmpty(s)) {
        printf("栈为空,无栈顶元素!\n");
        return -1;
    }
    return s->data[s->top];
}

// 移动盘子
void moveDisk(Stack* source, Stack* destination, char sourceName, char destinationName) {
    int disk = pop(source);
    push(destination, disk);
    printf("将盘子 %d 从 %c 移动到 %c\n", disk, sourceName, destinationName);
}

// 汉诺塔问题
void hanoi(int n, Stack* source, Stack* auxiliary, Stack* destination, char sourceName, char auxiliaryName, char destinationName) {
    Stack stack;
    initStack(&stack);

    // 初始状态入栈
    push(&stack, n);
    push(&stack, (int)sourceName);
    push(&stack, (int)auxiliaryName);
    push(&stack, (int)destinationName);
    push(&stack, (int)source);
    push(&stack, (int)auxiliary);
    push(&stack, (int)destination);

    while (!isEmpty(&stack)) {
        destination = (Stack*)pop(&stack);
        auxiliary = (Stack*)pop(&stack);
        source = (Stack*)pop(&stack);
        destinationName = (char)pop(&stack);
        auxiliaryName = (char)pop(&stack);
        sourceName = (char)pop(&stack);
        n = pop(&stack);

        if (n == 1) {
            moveDisk(source, destination, sourceName, destinationName);
        }
        else {
            // 模拟递归调用,按相反顺序入栈
            push(&stack, n - 1);
            push(&stack, (int)auxiliary);
            push(&stack, (int)source);
            push(&stack, (int)destination);
            push(&stack, (int)auxiliaryName);
            push(&stack, (int)sourceName);
            push(&stack, (int)destinationName);

            push(&stack, 1);
            push(&stack, (int)source);
            push(&stack, (int)auxiliary);
            push(&stack, (int)destination);
            push(&stack, (int)sourceName);
            push(&stack, (int)auxiliaryName);
            push(&stack, (int)destinationName);

            push(&stack, n - 1);
            push(&stack, (int)source);
            push(&stack, (int)destination);
            push(&stack, (int)auxiliary);
            push(&stack, (int)sourceName);
            push(&stack, (int)destinationName);
            push(&stack, (int)auxiliaryName);
        }
    }
}

int main() {
    int n = 3;
    Stack source, auxiliary, destination;
    initStack(&source);
    initStack(&auxiliary);
    initStack(&destination);

    // 初始化源柱子的盘子
    for (int i = n; i > 0; i--) {
        push(&source, i);
    }

    hanoi(n, &source, &auxiliary, &destination, 'A', 'B', 'C');

    return 0;
}

⑤ 栈的应用场景

1. 函数调用和递归
  • 原理:在程序执行过程中,当调用一个函数时,系统会将当前函数的上下文信息(如局部变量、返回地址等)压入栈中,形成一个栈帧。当函数执行完毕后,系统会从栈中弹出栈帧,恢复上一个函数的上下文继续执行。递归函数的调用也是基于栈的机制,每一次递归调用都会创建一个新的栈帧。
  • 示例:计算阶乘的递归函数,每次递归调用时都会将当前的参数和返回地址压入栈中,直到达到递归终止条件,然后依次从栈中弹出栈帧进行计算。
python 复制代码
def factorial(n):
    if n == 0 or n == 1:
        return 1
    else:
        return n * factorial(n-1)
2. 表达式求值
  • 原理:在计算中缀表达式的值时,通常会先将中缀表达式转换为后缀表达式(逆波兰表达式),然后利用栈来计算后缀表达式的值。在转换过程中,栈用于处理运算符的优先级;在计算过程中,栈用于存储操作数。
  • 示例 :对于中缀表达式 3 + 4 * 2,转换为后缀表达式 3 4 2 * + 后,使用栈进行计算。遇到操作数时将其压入栈,遇到运算符时从栈中弹出相应数量的操作数进行计算,并将结果压入栈。
3. 浏览器的后退功能
  • 原理:浏览器会将用户访问的网页地址依次压入栈中。当用户点击后退按钮时,浏览器会从栈中弹出最近访问的网页地址,并显示该网页。
  • 示例:用户依次访问了网页 A、网页 B、网页 C,浏览器的栈中依次压入 A、B、C。当用户点击后退按钮时,栈中弹出 C,显示网页 B。
4. 编辑器的撤销操作
  • 原理:编辑器会将用户的每一步操作(如输入、删除、修改等)依次压入栈中。当用户执行撤销操作时,编辑器会从栈中弹出最近的操作,并将其反向执行,以恢复到上一个状态。
  • 示例:用户在文本编辑器中输入了 "Hello",然后又输入了 " World",编辑器的栈中依次压入 "输入 Hello"、"输入 World"。当用户点击撤销按钮时,栈中弹出 "输入 World",并将其反向执行,即删除 " World"。

五,队列的示例代码

①队列的基本操作

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define MAX_SIZE 100

// 定义队列结构体
typedef struct {
    int data[MAX_SIZE];
    int front;
    int rear;
} Queue;

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

// 判断队列是否为空
bool isEmpty(Queue* q) {
    return q->front == q->rear;
}

// 判断队列是否已满
bool isFull(Queue* q) {
    return (q->rear + 1) % MAX_SIZE == q->front;
}

// 入队操作
bool enqueue(Queue* q, int value) {
    if (isFull(q)) {
        printf("队列已满,无法入队!\n");
        return false;
    }
    q->data[q->rear] = value;
    q->rear = (q->rear + 1) % MAX_SIZE;
    return true;
}

// 出队操作
bool dequeue(Queue* q, int* value) {
    if (isEmpty(q)) {
        printf("队列为空,无法出队!\n");
        return false;
    }
    *value = q->data[q->front];
    q->front = (q->front + 1) % MAX_SIZE;
    return true;
}

// 获取队头元素
bool peek(Queue* q, int* value) {
    if (isEmpty(q)) {
        printf("队列为空,无队头元素!\n");
        return false;
    }
    *value = q->data[q->front];
    return true;
}

int main() {
    Queue q;
    initQueue(&q);

    enqueue(&q, 10);
    enqueue(&q, 20);
    enqueue(&q, 30);

    int value;
    if (peek(&q, &value)) {
        printf("队头元素是: %d\n", value);
    }

    if (dequeue(&q, &value)) {
        printf("出队元素是: %d\n", value);
    }

    return 0;
}

②打印队列调度

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

#define MAX_SIZE 100

// 定义队列结构体
typedef struct {
    char data[MAX_SIZE][50];  // 假设任务名最长为 50 个字符
    int front;
    int rear;
} Queue;

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

// 判断队列是否为空
bool isEmpty(Queue* q) {
    return q->front == q->rear;
}

// 判断队列是否已满
bool isFull(Queue* q) {
    return (q->rear + 1) % MAX_SIZE == q->front;
}

// 入队操作
bool enqueue(Queue* q, const char* task) {
    if (isFull(q)) {
        printf("队列已满,无法入队!\n");
        return false;
    }
    // 修改 strcpy_s 函数调用,添加目标缓冲区大小参数
    strcpy_s(q->data[q->rear], sizeof(q->data[q->rear]), task);
    q->rear = (q->rear + 1) % MAX_SIZE;
    printf("任务 %s 已加入打印队列。\n", task);
    return true;
}

// 出队操作
bool dequeue(Queue* q, char* task) {
    if (isEmpty(q)) {
        printf("队列为空,无法出队!\n");
        return false;
    }
    // 修改 strcpy_s 函数调用,添加目标缓冲区大小参数
    strcpy_s(task, sizeof(task), q->data[q->front]);
    q->front = (q->front + 1) % MAX_SIZE;
    printf("正在打印任务 %s。\n", task);
    return true;
}

// 获取队列大小
int get_queue_size(Queue* q) {
    return (q->rear - q->front + MAX_SIZE) % MAX_SIZE;
}

int main() {
    Queue printer;
    initQueue(&printer);

    enqueue(&printer, "文档1");
    enqueue(&printer, "文档2");
    enqueue(&printer, "文档3");

    printf("当前打印队列中有 %d 个任务。\n", get_queue_size(&printer));

    char task[50];
    dequeue(&printer, task);
    dequeue(&printer, task);
    dequeue(&printer, task);
    dequeue(&printer, task);

    return 0;
}

③ 队列的其他应用场景

1. 任务调度
  • 原理:在操作系统中,多个任务需要按顺序执行,队列可用来管理这些任务。新任务会被添加到队列尾部,调度器按顺序从队列头部取出任务执行,确保任务按到达顺序处理。
  • 示例:在多用户的计算机系统里,多个用户提交打印任务,操作系统会将这些任务存入一个队列。打印机按队列中任务的先后顺序依次打印,避免多个任务同时竞争打印机资源。
2. 网络数据传输
  • 原理:在网络通信中,数据通常以数据包的形式传输。发送方和接收方都可能使用队列来缓冲数据。发送方将待发送的数据包加入队列,网络接口按顺序从队列中取出数据包发送;接收方将接收到的数据包存入队列,上层应用程序按顺序从队列中读取数据包进行处理。
  • 示例:在 TCP 协议中,接收方使用接收缓冲区(队列)来存储接收到的数据包。当上层应用程序准备好处理数据时,从队列头部取出数据包进行解析。
3. 广度优先搜索(BFS)
  • 原理:BFS 是一种用于遍历或搜索树或图的算法,它从根节点(或起始节点)开始,逐层地访问节点。队列在 BFS 中用于存储待访问的节点。首先将起始节点加入队列,然后不断从队列头部取出节点进行访问,并将其未访问的邻接节点加入队列尾部。
  • 示例:在地图导航中,使用 BFS 算法可以找到从起点到终点的最短路径。将起点加入队列,然后依次访问队列中节点的邻接节点,直到找到终点。
4. 消息队列系统
  • 原理:消息队列系统用于在不同组件或服务之间传递消息。生产者将消息发送到队列中,消费者从队列中取出消息进行处理。队列确保消息按发送顺序被处理,同时实现了生产者和消费者之间的解耦。
  • 示例:在分布式系统中,多个微服务之间需要进行通信。一个微服务产生的消息可以放入消息队列,其他微服务可以从队列中获取消息并进行相应的处理,例如电商系统中,订单服务产生订单消息,库存服务从消息队列中获取消息并更新库存。

现实生活领域

1. 排队系统
  • 原理:在各种排队场景中,如银行、超市、餐厅等,人们按到达顺序排队等待服务。队列可以模拟这种排队机制,新顾客加入队列尾部,服务人员按顺序从队列头部为顾客提供服务。
  • 示例:在银行办理业务时,顾客取号后进入排队队列,柜员按队列顺序依次为顾客办理业务。
cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define MAX_SIZE 100

// 定义队列结构体
typedef struct {
    int customers[MAX_SIZE];
    int front;
    int rear;
} Queue;

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

// 判断队列是否为空
bool isEmpty(Queue *q) {
    return q->front == q->rear;
}

// 判断队列是否已满
bool isFull(Queue *q) {
    return (q->rear + 1) % MAX_SIZE == q->front;
}

// 入队操作
bool enqueue(Queue *q, int customer) {
    if (isFull(q)) {
        printf("队列已满,无法入队!\n");
        return false;
    }
    q->customers[q->rear] = customer;
    q->rear = (q->rear + 1) % MAX_SIZE;
    printf("顾客 %d 已加入排队队列。\n", customer);
    return true;
}

// 出队操作
bool dequeue(Queue *q, int *customer) {
    if (isEmpty(q)) {
        printf("队列为空,没有顾客等待服务!\n");
        return false;
    }
    *customer = q->customers[q->front];
    q->front = (q->front + 1) % MAX_SIZE;
    printf("正在为顾客 %d 提供服务。\n", *customer);
    return true;
}

// 获取队列大小
int getQueueSize(Queue *q) {
    return (q->rear - q->front + MAX_SIZE) % MAX_SIZE;
}

int main() {
    Queue queue;
    initQueue(&queue);

    // 顾客入队
    enqueue(&queue, 1);
    enqueue(&queue, 2);
    enqueue(&queue, 3);

    printf("当前排队队列中有 %d 位顾客。\n", getQueueSize(&queue));

    // 服务顾客
    int customer;
    dequeue(&queue, &customer);
    dequeue(&queue, &customer);
    dequeue(&queue, &customer);
    dequeue(&queue, &customer);

    return 0;
}    
2. 电梯系统
  • 原理:电梯系统中,乘客按下楼层按钮后,请求会被加入一个队列。电梯按队列中请求的顺序依次停靠相应楼层,确保乘客按请求顺序被服务。
  • 示例:在一栋大楼中,多个乘客在不同楼层按下电梯按钮,电梯控制系统将这些请求存入队列,电梯根据队列中的请求依次到达各个楼层。
cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define MAX_SIZE 100
#define MIN_FLOOR 1
#define MAX_FLOOR 10

// 定义队列结构体
typedef struct {
    int requests[MAX_SIZE];
    int front;
    int rear;
} Queue;

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

// 判断队列是否为空
bool isEmpty(Queue *q) {
    return q->front == q->rear;
}

// 判断队列是否已满
bool isFull(Queue *q) {
    return (q->rear + 1) % MAX_SIZE == q->front;
}

// 入队操作
bool enqueue(Queue *q, int floor) {
    if (isFull(q)) {
        printf("请求队列已满,无法添加新请求!\n");
        return false;
    }
    if (floor < MIN_FLOOR || floor > MAX_FLOOR) {
        printf("无效的楼层请求:%d。楼层范围为 %d - %d。\n", floor, MIN_FLOOR, MAX_FLOOR);
        return false;
    }
    q->requests[q->rear] = floor;
    q->rear = (q->rear + 1) % MAX_SIZE;
    printf("已收到前往 %d 层的请求。\n", floor);
    return true;
}

// 出队操作
bool dequeue(Queue *q, int *floor) {
    if (isEmpty(q)) {
        printf("请求队列为空,没有请求需要处理!\n");
        return false;
    }
    *floor = q->requests[q->front];
    q->front = (q->front + 1) % MAX_SIZE;
    printf("电梯正在前往 %d 层。\n", *floor);
    return true;
}

// 获取队列大小
int getQueueSize(Queue *q) {
    return (q->rear - q->front + MAX_SIZE) % MAX_SIZE;
}

int main() {
    Queue elevatorQueue;
    initQueue(&elevatorQueue);

    // 乘客发出请求
    enqueue(&elevatorQueue, 3);
    enqueue(&elevatorQueue, 7);
    enqueue(&elevatorQueue, 12);  // 无效请求

    printf("当前电梯请求队列中有 %d 个请求。\n", getQueueSize(&elevatorQueue));

    // 电梯处理请求
    int floor;
    dequeue(&elevatorQueue, &floor);
    dequeue(&elevatorQueue, &floor);
    dequeue(&elevatorQueue, &floor);

    return 0;
}    
相关推荐
似水এ᭄往昔2 小时前
【数据结构】——单链表练习(1)
数据结构
whoarethenext2 小时前
数据结构堆的c/c++的实现
c语言·数据结构·c++·
n33(NK)3 小时前
【算法基础】选择排序算法 - JAVA
数据结构·算法·排序算法
CS创新实验室4 小时前
408考研逐题详解:2009年第6题
数据结构·考研·算法·408·真题·计算机考研·408计算机
阳洞洞5 小时前
leetcode 142. Linked List Cycle II
数据结构·leetcode·链表·双指针
apcipot_rain5 小时前
【计算机网络 第8版】谢希仁编著 第四章网络层 地址类题型总结
数据结构·算法
NON-JUDGMENTAL7 小时前
第2章 算法分析基础
java·数据结构·算法
wen__xvn7 小时前
每日一题洛谷P1025 [NOIP 2001 提高组] 数的划分c++
数据结构·c++·算法
草莓啵啵~7 小时前
数据结构--树
数据结构
MeiYu_1238 小时前
【数据结构与算法】常见排序算法详解(C++实现)
数据结构·c++·算法·排序算法