数据结构与算法笔记—栈与队列

此文章为本人学习笔记,若有错误求指正。

一、栈(Stack)

1. 栈的基本概念

栈(Stack)是一种操作受限的线性表,遵循“后进先出”(LIFO, Last In, First Out)原则。栈只能在一端进行插入和删除操作,这一端被称为栈顶(Top)。最早进入栈的元素被压在栈底,最后进入的元素位于栈顶,最先被取出。

2. 栈的基本操作

  • Push(压栈):将元素压入栈顶。
  • Pop(出栈):从栈顶弹出元素。
  • Peek(查看栈顶元素):获取栈顶元素的值,但不弹出元素。
  • IsEmpty(判断栈是否为空):检查栈是否为空。
  • Clear(清空栈):移除栈中的所有元素。

3. 栈的应用场景

  • 浏览器前进、后退功能:使用栈来保存用户的浏览历史,实现前进和后退操作。
  • 编译器与进程:函数调用时,栈用来保存函数调用信息(如局部变量、返回地址等)。
  • 括号匹配与语法分析:栈用于检测编程语言中的括号匹配问题。
  • 表达式转换:中缀表达式转后缀表达式使用栈来保存操作符。
  • 后缀表达式求值:使用栈来求解后缀表达式。
  • 进制转换:将数字从一种进制转换到另一种进制时,使用栈来保存中间结果。

4. 栈的实现

栈可以通过两种方式实现:顺序栈(基于数组)和链式栈(基于链表)。

4.1 顺序栈

顺序栈使用数组来存储栈中的元素。由于数组的大小固定,因此顺序栈的容量是有限的。在实现时,需要一个变量来记录栈顶元素的索引。

顺序栈的实现代码

#include <stdio.h>
#include <stdlib.h>

#define MAX_SIZE 100

typedef struct {
    int data[MAX_SIZE];
    int top;
} Stack;

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

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

// 压栈
void push(Stack *stack, int value) {
    if (stack->top == MAX_SIZE - 1) {
        printf("栈满,无法压栈。\n");
        return;
    }
    stack->data[++(stack->top)] = value;
}

// 出栈
int pop(Stack *stack) {
    if (isEmpty(stack)) {
        printf("栈空,无法出栈。\n");
        exit(1);
    }
    return stack->data[(stack->top)--];
}

// 查看栈顶元素
int peek(Stack *stack) {
    if (isEmpty(stack)) {
        printf("栈空。\n");
        exit(1);
    }
    return stack->data[stack->top];
}

// 清空栈
void clear(Stack *stack) {
    stack->top = -1;
}

4.2 链式栈

链式栈使用链表来实现。链表中的每个节点包含一个数据元素和指向下一个节点的指针。链式栈没有容量限制,可以根据需要动态扩展。

链式栈的实现代码

#include <stdio.h>
#include <stdlib.h>

// 链式栈节点结构
typedef struct Node {
    int data;
    struct Node *next;
} Node;

// 栈结构
typedef struct {
    Node *top;
} Stack;

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

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

// 压栈
void push(Stack *stack, int value) {
    Node *newNode = (Node *)malloc(sizeof(Node));
    if (!newNode) {
        printf("内存分配失败。\n");
        exit(1);
    }
    newNode->data = value;
    newNode->next = stack->top;
    stack->top = newNode;
}

// 出栈
int pop(Stack *stack) {
    if (isEmpty(stack)) {
        printf("栈空,无法出栈。\n");
        exit(1);
    }
    Node *temp = stack->top;
    int value = temp->data;
    stack->top = temp->next;
    free(temp);
    return value;
}

// 查看栈顶元素
int peek(Stack *stack) {
    if (isEmpty(stack)) {
        printf("栈空。\n");
        exit(1);
    }
    return stack->top->data;
}

// 清空栈
void clear(Stack *stack) {
    while (!isEmpty(stack)) {
        pop(stack);
    }
}

二、队列(Queue)

1. 队列的基本概念

队列(Queue)是一种操作受限的线性表,遵循“先进先出”(FIFO, First In, First Out)原则。队列允许在一端(队尾)插入元素,在另一端(队头)删除元素。队列中的第一个元素被最早插入,最后被删除。

2. 队列的基本操作

  • Enqueue(入队):将元素插入队尾。
  • Dequeue(出队):从队头删除元素。
  • Peek(查看队头元素):获取队头元素的值,但不删除元素。
  • IsEmpty(判断队列是否为空):检查队列是否为空。
  • Clear(清空队列):移除队列中的所有元素。

3. 队列的应用场景

  • 网络请求:处理网络请求时,使用队列来存储等待处理的请求。
  • 事件处理:GUI系统中,将事件存入队列,然后依次分发处理。
  • 消息队列(MQ):消息队列在异步通信中被广泛使用,如Kafka等消息中间件。
  • 线程池:线程池使用队列来管理和调度线程任务。

4. 队列的实现

队列可以通过两种方式实现:顺序队列(基于数组)和链式队列(基于链表)。

4.1 顺序队列

顺序队列使用数组来存储元素,并通过两个指针分别指向队头和队尾。为了有效利用数组空间,常使用循环队列的形式来避免“假溢出”。

顺序队列的实现代码

#include <stdio.h>
#include <stdlib.h>

#define MAX_SIZE 100

typedef struct {
    int data[MAX_SIZE];
    int front;
    int rear;
} Queue;

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

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

// 入队
void enqueue(Queue *queue, int value) {
    if ((queue->rear + 1) % MAX_SIZE == queue->front) {
        printf("队列满,无法入队。\n");
        return;
    }
    queue->data[queue->rear] = value;
    queue->rear = (queue->rear + 1) % MAX_SIZE;
}

// 出队
int dequeue(Queue *queue) {
    if (isEmpty(queue)) {
        printf("队列空,无法出队。\n");
        exit(1);
    }
    int value = queue->data[queue->front];
    queue->front = (queue->front + 1) % MAX_SIZE;
    return value;
}

// 查看队头元素
int peek(Queue *queue) {
    if (isEmpty(queue)) {
        printf("队列空。\n");
        exit(1);
    }
    return queue->data[queue->front];
}

// 清空队列
void clear(Queue *queue) {
    queue->front = queue->rear = 0;
}

4.2 链式队列

链式队列使用链表来实现,每个节点包含一个数据元素和指向下一个节点的指针。链式队列不受限于数组的固定容量,可以根据需要动态扩展。

链式队列的实现代码

#include <stdio.h>
#include <stdlib.h>

// 链式队列节点结构
typedef struct Node {
    int data;
    struct Node *next;
} Node;

// 队列结构
typedef struct {
    Node *front;
    Node *rear;
} Queue;

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

// 判断队列是否为空
int isEmpty(Queue *queue) {
    return queue->front == NULL;
}

// 入队
void enqueue(Queue *queue, int value) {
    Node *newNode = (Node *)malloc(sizeof(Node));
    if (!newNode) {
        printf("内存分配失败。\n");
        exit(1);
    }
    newNode->data = value;
    newNode->next = NULL;
    if (isEmpty(queue)) {
        queue->front = queue->rear = newNode;
    } else {
        queue->rear->next = newNode;
        queue->rear = newNode;
    }
}

// 出队
int dequeue(Queue *queue) {
    if (isEmpty(queue)) {
        printf("队列空,无法出队。\n");
        exit(1);
    }
    Node *temp = queue->front;
    int value = temp->data;
    queue->front = temp->next;
    if (queue->front == NULL) {
        queue->rear = NULL;
    }
    free(temp);
    return value;
}

// 查看队头元素
int peek(Queue *queue) {
    if (isEmpty(queue)) {
        printf("队列空。\n");
        exit(1);
    }
    return queue->front->data;
}

// 清空队列
void clear(Queue *queue) {
    while (!isEmpty(queue)) {
        dequeue(queue);
    }
}

三、总结

栈和队列作为操作受限的线性数据结构,在算法设计和系统开发中有着广泛的应用。栈以其后进先出的特性,在函数调用、表达式求值、括号匹配等场景中扮演重要角色。队列则因其先进先出的特性,广泛用于任务调度、消息传递等场合。通过对栈和队列的深入理解和实现,可以为掌握更复杂的数据结构和算法打下坚实的基础。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值