数据结构与算法 —— 表(栈、队列)

一. 栈(stack)

栈(Stack)是一种线性数据结构,它遵循**后进先出(LIFO, Last In First Out)**的原则。换句话说,最新添加到栈中的元素将最先被移除,而最早添加的元素最后被移除。这种特性使得栈在许多算法和系统操作中具有重要作用。

1.栈的基本操作

栈通常支持以下几种基本操作:

  1. 入栈(Push):

    • 将一个元素添加到栈的顶端。
    • 如果栈有大小限制,则在栈满时可能无法进行入栈操作。
  2. 出栈(Pop):

    • 移除并返回栈顶元素。
    • 如果栈为空,出栈操作通常会返回一个错误或特殊的值(例如,null-1),表示栈为空。
  3. 查看栈顶元素(Peek/Top):

    • 返回栈顶元素但不移除它。
    • 这个操作通常用于查看栈顶元素而不影响栈的内容。
  4. 判空(IsEmpty):

    • 检查栈是否为空。如果为空,返回 true,否则返回 false

2.栈的应用场景

由于栈的 LIFO 特性,它在许多场景中被广泛应用:

  1. 函数调用堆栈:

    • 在程序执行过程中,每次函数调用都会在栈中创建一个新的栈帧(包含函数的局部变量、参数等),当函数返回时,该栈帧会被弹出。
  2. 表达式求值与语法解析:

    • 在编译器中,栈用于中缀表达式转后缀表达式(逆波兰表示法)以及表达式的求值。
    • 例如,处理带括号的数学表达式时,栈可以帮助解析表达式的优先级。
  3. 撤销操作:

    • 许多软件(例如文本编辑器)通过栈来实现“撤销”功能。每次操作都会被记录在栈中,当用户选择撤销时,栈顶的操作会被反向执行并移除。
  4. 深度优先搜索(DFS):

    • 栈可以用来实现图的深度优先搜索算法。

3.栈的实现

栈可以通过两种主要方式实现:

  1. 数组实现(静态栈):

    • 使用固定大小的数组实现栈结构。栈顶的索引随着入栈和出栈操作而变化。
    • 优点:实现简单,操作速度快。
    • 缺点:栈的大小是固定的,可能会出现溢出问题。
  2. 链表实现(动态栈):

    • 使用链表节点来实现栈,每个节点包含一个数据元素和指向下一个节点的指针。
    • 优点:栈的大小不受固定限制,只要内存允许,可以动态增长。
    • 缺点:需要额外的内存来存储指针,操作速度相对数组实现稍慢。

4.代码实现

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


typedef struct stack
{
    int *data;      //数组
    int capacity;   //容量
    int top;        //栈顶索引
}Stack;

void init(Stack *s);
void push(Stack *s, int value);
//出栈,删除栈顶的值
int pop(Stack *s);
//读取,不删除
int peek(Stack *s);
int size(Stack *s);

int main(int argc, char const *argv)
{
    Stack *s = malloc(sizeof(Stack));
    init(s);
    push(s, 100);
    push(s, 200);
    push(s, 300);
    
    printf("%d\n",size(s));
    printf("%d\n", pop(s));
    printf("%d\n", pop(s));
    printf("%d\n", pop(s));
    printf("%d\n",size(s));
    return 0;
}

void init(Stack *s)
{
    s->capacity = 8;
    s->data = malloc(sizeof(int) * s->capacity);
    s->top = -1;
}

void push(Stack *s, int value)
{
    //是否满
    s->data[++s->top] = value;
}

int pop(Stack *s)
{
    if (s->top == -1)
    {
        printf("栈为空\n");
    }
    else
        return s->data[s->top--]; 
}

int size(Stack *s)
{
    return s->top + 1;
}

int peek(Stack *s)
{
    if (s->top == -1)
    {
        printf("栈为空\n");
    }
    else
        return s->data[s->top];
}

二. 队列(queue)

队列是一种先进先出(FIFO, First-In-First-Out)的数据结构。它类似于现实生活中的排队规则,即最先排队的人最先被服务。在计算机科学中,队列被广泛用于各种应用场景,如任务调度、数据缓冲等。

1.队列的基本操作

  1. 入队(Enqueue):

    将一个元素添加到队列的尾部。
  2. 出队(Dequeue):

    移除并返回队列的头部元素。因为是 FIFO 结构,最早进入队列的元素最先被移除。
  3. 查看队头元素(Peek or Front):

    返回队列的头部元素,但不移除它。
  4. 检查队列是否为空:

    判断队列中是否有元素。
  5. 检查队列是否已满(对于固定大小的队列):

    判断队列是否已达到其容量限制。

2.队列的应用场景

  1. 任务调度:

    在操作系统中,任务调度器通常使用队列来管理等待执行的任务。
  2. 打印队列:

    打印机通常会将待打印的文件放入队列中,按照顺序逐一处理。
  3. 广度优先搜索(BFS):

    在图或树的广度优先搜索算法中,队列用于按层次顺序存储节点。
  4. 数据流处理:

    在流式数据处理中,队列用于缓存数据以便按顺序处理。

3.队列的实现

队列可以通过多种方式实现,最常见的有以下几种:

  1. 数组实现:

    • 使用数组来保存队列中的元素。通过两个指针(或索引)来指示队列的头部和尾部。当元素入队时,尾部指针向后移动;当元素出队时,头部指针向后移动。
    • 如果是循环队列,则队列满时,尾部指针回到数组的起始位置。
  2. 链表实现:

    使用链表来实现队列,链表的头部作为队列的前端,尾部作为队列的后端。链表的实现方式灵活,不需要担心队列的容量问题。

4.代码实现

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

#define MAX_SIZE 5  // 队列的最大容量

typedef struct Queue {
    int data[MAX_SIZE]; // 存储队列元素的数组
    int front;  // 指向队列头部的索引
    int rear;   // 指向队列尾部的索引
    int size;   // 当前队列中的元素数量
} Queue;

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

// 检查队列是否为空
int isEmpty(Queue *q) {
    return q->size == 0;
}

// 检查队列是否已满
int isFull(Queue *q) {
    return q->size == MAX_SIZE;
}

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

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

// 查看队头元素
int peek(Queue *q) {
    if (isEmpty(q)) {
        printf("队列为空,无法查看队头元素\n");
        exit(EXIT_FAILURE);
    }
    return q->data[q->front];
}

// 获取队列的大小
int size(Queue *q) {
    return q->size;
}

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

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

    printf("队列大小: %d\n", size(&q));
    printf("队头元素: %d\n", peek(&q));

    while (!isEmpty(&q)) {
        printf("出队元素: %d\n", dequeue(&q));
    }

    return 0;
}

三.总结

  • 数据结构特性:

    • 队列: FIFO 结构,适用于按顺序处理的任务。
    • : LIFO 结构,适用于需要倒序处理的任务。
  • 实现方式:

    • 两者都可以通过数组或链表来实现。队列需要两个指针(队头和队尾),而栈只需一个指针(栈顶)。
  • 应用场景:

    • 队列通常用于调度、广度优先搜索等需要按顺序处理的任务。
    • 栈则常用于递归、表达式求值等需要倒序或回溯处理的任务。

两者都是非常重要的基础数据结构,在计算机科学和编程中有广泛的应用。

如有错误,欢迎批评指正。

  • 24
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值