特性 | 栈(Stack) | 队列(Queue) |
---|---|---|
定义 | 仅允许在栈顶(表尾)进行插入和删除的线性表,遵循 后进先出(LIFO)。 | 允许在队尾插入、队头删除的线性表,遵循 先进先出(FIFO)。 |
操作限制 | 插入(入栈 / Push)和删除(出栈 / Pop)只能在栈顶进行。 | 插入(入队 / Enqueue)在队尾,删除(出队 / Dequeue)在队头。 |
典型场景 | 适合 “后处理先完成” 的场景,如撤销操作、函数调用栈。 | 适合 “先处理先完成” 的场景,如任务排队、消息缓冲。 |
操作 | 栈 | 队列 |
---|---|---|
插入位置 | 栈顶(表尾),新元素成为新栈顶。 | 队尾,新元素追加到队列末尾。 |
删除位置 | 栈顶,删除最后插入的元素(LIFO)。 | 队头,删除最早插入的元素(FIFO)。 |
核心操作 | Push(入栈)、Pop(出栈)、GetTop(取栈顶)。 | Enqueue(入队)、Dequeue(出队)、GetHead(取队头)。 |
时间复杂度 | 均为 O(1)(仅修改栈顶指针)。 | 均为 O(1)(仅修改队头 / 队尾指针)。 |
栈:
top
指针指向栈顶(下标),栈满时top = MAXSIZE-1
,栈空时top = -1
。
队列:
front
(队头)和rear
(队尾下一个位置)指针管理,通过取模运算实现循环,解决假溢出问题。
(rear + 1) % MAXSIZE == front
(牺牲一个单元区分空和满)。plaintext
栈顶 → [top] 新元素(入栈)/ 旧元素(出栈)
栈底 → [bottom](固定端)
plaintext
队头 → [front] 旧元素(出队) ← 队尾 [rear] 新元素(入队)
场景 | 栈的应用 | 队列的应用 |
---|---|---|
函数调用 | 存储函数调用的参数、返回地址(系统栈自动管理)。 | 无直接应用,函数调用本质是栈结构。 |
表达式处理 | 后缀表达式求值、中缀转后缀(处理运算符优先级)。 | 无相关应用。 |
括号匹配 | 检测括号是否正确嵌套(如{[()]} )。 |
无相关应用。 |
任务调度 | 无直接应用,适合 “逆序处理”(如撤销操作)。 | 打印机任务队列、操作系统进程调度(FIFO)。 |
数据遍历 | 深度优先搜索(DFS,递归本质是栈)。 | 广度优先搜索(BFS,逐层遍历图或树)。 |
缓冲区管理 | 无直接应用。 | 输入输出缓冲区(如键盘输入、网络数据接收)。 |
操作顺序:
适用场景:
数据结构差异:
递归是指函数直接或间接调用自身的过程,其核心依赖系统栈(调用栈)来保存每次调用的状态(参数、局部变量、返回地址),确保函数能正确返回并恢复上下文。
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)
),且重复计算效率低(可通过记忆化优化)。特性 | 递归(系统栈) | 手动栈(用户实现) |
---|---|---|
管理方式 | 系统自动管理调用帧,无需手动操作。 | 需要手动压栈、出栈(如表达式求值)。 |
适用场景 | 适合树形 / 分治问题(如 DFS、回溯),代码简洁。 | 适合显式需要 LIFO 的场景(如表达式转换)。 |
风险 | 可能栈溢出(递归深度超过系统栈容量)。 | 需手动处理栈满 / 栈空逻辑。 |
3 + 4 * 2 - 1
),符合人类习惯,但计算机处理需要考虑优先级和括号。3 4 2 * + 1 -
),无需括号,计算机可直接求值。- + 3 * 4 2 1
),较少使用。(
:压栈。)
:弹出栈中运算符直到遇到左括号(左括号不加入后缀表达式)。* / %
> + -
> (
)
(括号仅用于界定优先级,本身无优先级)。
3 + 4 * 2 - 1
转后缀3
:操作数,直接输出 → 3
+
:栈空,压栈 → 栈:+
4
:操作数,输出 → 3 4
*
:优先级高于栈顶+
,压栈 → 栈:+ *
2
:操作数,输出 → 3 4 2
-
:优先级低于栈顶*
,弹出*
→ 输出3 4 2 *
,栈:+
;再比较-
与+
优先级相等,弹出+
→ 输出3 4 2 * +
,栈空,压栈-
1
:操作数,输出 → 3 4 2 * + 1
-
→ 最终后缀表达式:3 4 2 * + 1 -
3 4 2 * + 1 -
求值3
:压栈 → 栈:[3]
4
:压栈 → 栈:[3, 4]
2
:压栈 → 栈:[3, 4, 2]
*
:弹出2
和4
,计算4×2=6
,压栈 → 栈:[3, 6]
+
:弹出6
和3
,计算3+6=9
,压栈 → 栈:[9]
1
:压栈 → 栈:[9, 1]
-
:弹出1
和9
,计算9-1=8
,压栈 → 最终结果:8
① 栈的基本操作
#include
#include
#include
#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;
}
② 栈的应用实例
#include
#include
#include
#include
#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;
}
③使用栈实现汉诺塔问题
#include
#include
#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;
}
⑤ 栈的应用场景
def factorial(n):
if n == 0 or n == 1:
return 1
else:
return n * factorial(n-1)
3 + 4 * 2
,转换为后缀表达式 3 4 2 * +
后,使用栈进行计算。遇到操作数时将其压入栈,遇到运算符时从栈中弹出相应数量的操作数进行计算,并将结果压入栈。①队列的基本操作
#include
#include
#include
#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;
}
②打印队列调度
#include
#include
#include
#include
#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;
}
③ 队列的其他应用场景
#include
#include
#include
#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;
}
#include
#include
#include
#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;
}