当前位置: 首页 > article >正文

栈和队列的数据结构

1. 栈 (Stack)

栈是一种后进先出 (LIFO, Last In First Out) 的数据结构。也就是说,最后加入栈中的元素最先被移除。

栈的基本操作:
  • 入栈 (Push):将元素放入栈顶。
  • 出栈 (Pop):移除并返回栈顶的元素。
  • 查看栈顶 (Peek/Top):返回栈顶的元素但不移除它。
  • 判空 (IsEmpty):判断栈是否为空。
  • 栈的大小 (Size):返回栈中元素的数量。
栈的应用:
  • 递归调用:函数调用的管理,操作系统使用栈来管理函数的递归调用。
  • 表达式求值:例如,使用栈来求解中缀表达式转换为后缀表达式,或直接求解后缀表达式。
  • 括号匹配:判断括号的配对是否合法,通常通过栈实现。

 

2. 队列 (Queue)

队列是一种先进先出 (FIFO, First In First Out) 的数据结构。即最先加入队列的元素最先被移除。

队列的基本操作:
  • 入队 (Enqueue):将元素添加到队尾。
  • 出队 (Dequeue):移除并返回队头的元素。
  • 查看队头 (Front/Peek):返回队头的元素但不移除它。
  • 判空 (IsEmpty):判断队列是否为空。
  • 队列的大小 (Size):返回队列中元素的数量。
队列的应用:
  • 任务调度:例如,操作系统中的任务调度、进程调度等。
  • 广度优先搜索 (BFS):使用队列实现广度优先遍历。
  • 缓冲区:网络数据传输、流媒体播放等使用队列来存储和处理数据流。
队列的变种:
  • 双端队列 (Deque):允许在队列两端进行入队和出队操作。
  • 优先队列 (Priority Queue):每个元素有优先级,优先级高的元素会优先出队。

1. 操作方式的区别

  • 栈 (Stack):遵循后进先出 (LIFO) 原则。最后进入栈的元素最先被移除。
    • 特点:只能在一端(栈顶)进行插入和删除操作。
  • 队列 (Queue):遵循先进先出 (FIFO) 原则。最先进入队列的元素最先被移除。
    • 特点:在一端(队尾)进行插入操作,在另一端(队头)进行删除操作。

2. 插入与删除位置的区别

  • :只允许在栈顶进行插入删除操作。
  • 队列:在队尾插入,在队头删除

3. 数据流向的区别

  • :数据在同一端进出,具有“递归”性质,常用于需要回溯的场景。
  • 队列:数据从队尾进入,从队头离开,常用于顺序处理任务的场景。

4. 主要应用场景的区别

  • 栈的应用场景

    • 函数调用管理:递归调用时,函数返回值、局部变量保存在栈中。
    • 表达式求值:中缀表达式转后缀表达式、后缀表达式求值。
    • 括号匹配:验证表达式中括号的合法性。
    • 深度优先搜索 (DFS):使用栈实现图的深度优先遍历。
  • 队列的应用场景

    • 任务调度:操作系统中的进程调度、打印任务调度等。
    • 广度优先搜索 (BFS):用队列来进行层次遍历。
    • 数据缓冲区:例如在网络通信或流媒体处理中,用于存储和处理数据流。
    • 事件处理系统:使用队列按顺序处理事件(例如消息队列)。

5. 数据存取顺序的区别

  • :后进先出。即最近存入的数据最先被取出。
  • 队列:先进先出。即最早存入的数据最先被取出。

 

特性栈 (Stack)队列 (Queue)
数据存储顺序后进先出 (LIFO)先进先出 (FIFO)
操作位置仅在栈顶队尾插入,队头删除
插入复杂度O(1)O(1)
删除复杂度O(1)O(1)
主要用途递归、回溯、表达式求值任务调度、广度优先搜索

 

使用链表实现栈和队列的操作,可以充分发挥链表动态分配内存的特性,避免数组实现中需要预定义大小的限制。


一、使用链表实现栈

栈遵循后进先出 (LIFO) 的原则,因此我们可以使用单向链表的头部作为栈的栈顶,实现入栈、出栈和查看栈顶的操作。

栈的链表节点结构:
typedef struct Node {
    int data;
    struct Node* next;
} Node;
栈的常见操作:
1.入栈 (Push): 每次将新元素插入到链表的头部,这样插入的元素就相当于在栈顶。
void push(Node** top, int value) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    newNode->data = value;
    newNode->next = *top;
    *top = newNode;
}
2. 出栈 (Pop): 每次从链表的头部移除元素,相当于弹出栈顶元素。如果链表为空,表示栈为空。
int pop(Node** top) {
    if (*top == NULL) {
        printf("Stack Underflow\n");
        return -1;  // 空栈的特殊情况处理
    }
    Node* temp = *top;
    int poppedValue = temp->data;
    *top = (*top)->next;
    free(temp);
    return poppedValue;
}
3. 查看栈顶 (Peek/Top): 返回当前栈顶的值,但不移除它。
int peek(Node* top) {
    if (top == NULL) {
        printf("Stack is empty\n");
        return -1;
    }
    return top->data;
}
4.判空 (IsEmpty): 检查栈是否为空,即链表是否为空。
int isEmpty(Node* top) {
    return top == NULL;
}

二、使用链表实现队列

队列遵循先进先出 (FIFO) 的原则,因此使用链表实现时,可以让链表的头部作为队列的队头,链表的尾部作为队尾。这样插入操作发生在队尾,删除操作发生在队头。

队列的链表节点结构:
typedef struct Node {
    int data;
    struct Node* next;
} Node;

typedef struct Queue {
    Node* front;
    Node* rear;
} Queue;

 

队列的常见操作:
1.初始化队列 (InitQueue): 初始化队列时,将 frontrear 都设置为 NULL,表示空队列。
void initQueue(Queue* q) {
    q->front = q->rear = NULL;
}
2. 入队 (Enqueue): 将新元素插入到链表的尾部。如果队列为空,frontrear 都指向新节点。
void enqueue(Queue* q, int value) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    newNode->data = value;
    newNode->next = NULL;
    if (q->rear == NULL) {  // 队列为空
        q->front = q->rear = newNode;
        return;
    }
    q->rear->next = newNode;
    q->rear =
3.出队 (Dequeue): 从链表的头部移除元素,即移除队列的队头。如果队列为空,则返回特定的错误值。移除队头后,需要更新 front 指针。如果移除后队列为空,还需要将 rear 也设置为 NULL
int dequeue(Queue* q) {
    if (q->front == NULL) {  // 队列为空
        printf("Queue Underflow\n");
        return -1;  // 空队列时的特殊处理
    }

    Node* temp = q->front;
    int dequeuedValue = temp->data;
    q->front = q->front->next;

    // 如果队列变为空,更新 rear 为 NULL
    if (q->front == NULL) {
        q->rear = NULL;
    }

    free(temp);
    return dequeuedValue;
}
4. 查看队头 (Front/Peek): 返回队列队头的元素但不移除它。如果队列为空,返回特定的错误值。
int peek(Queue* q) {
    if (q->front == NULL) {
        printf("Queue is empty\n");
        return -1;
    }
    return q->front->data;
}
5. 判空 (IsEmpty): 检查队列是否为空,即 front 指针是否为 NULL
int isEmpty(Queue* q) {
    return q->front == NULL;
}
 6.队列大小 (Size): 由于链表的实现不会像数组那样直接知道大小,所以需要遍历整个链表来计算队列的元素个数。
int size(Queue* q) {
    int count = 0;
    Node* current = q->front;
    while (current != NULL) {
        count++;
        current = current->next;
    }
    return count;
}

 


http://www.kler.cn/a/293731.html

相关文章:

  • 软件测试面试八股文(超详细整理)
  • Kubernetes在容器编排中的应用
  • python实战(八)——情感识别(多分类)
  • 3.5【数据库系统】ER图
  • 《TCP/IP网络编程》学习笔记 | Chapter 11:进程间通信
  • 矩阵的各种计算:乘法、逆矩阵、转置、行列式等——基于Excel实现
  • ASP.NET Core 入门教学八 集成RocketMQ消息队列
  • json字符串CSS格式化
  • 【python因果推断库12】工具变量回归与使用 pymc 验证工具变量5
  • DDoS对策是什么?详细解说DDoS攻击难以防御的理由和对策方法
  • Docker进入容器并运行命令
  • 【学习笔记】SSL证书安全机制之证书撤销
  • Docker 安装 MySQL 8.0 并支持远程访问
  • jmeter之循环控制器使用
  • 校园圈子论坛小程序如何搭建?校园多功能系统源码实现
  • 正点原子阿尔法ARM开发板-IMX6ULL(二)——介绍情况以及汇编
  • 基于飞腾平台的Hive的安装配置
  • 从贝叶斯角度理解卡尔曼滤波算法
  • 狂奔的荣耀,稳健的苹果:AI Agent手机竞速赛
  • Linux平台屏幕|摄像头采集并实现RTMP推送两种技术方案探究
  • 使用isolation: isolate声明隔离混合模式
  • 超声波测距模块HC-SR04(基于STM32F103C8T6HAL库)
  • 比较差异 图片 视频
  • 问题合集更更更之cssnano配置导致打包重新计算z-index
  • 中秋猜灯谜_猜字谜小程序源码,无需服务器
  • 目标检测-YOLOv8