C语言-栈和队列

在这里插入图片描述

文章目录

  • 🎯引言
  • 👓栈和队列
    • 1.栈
      • 1.1栈的概念与结构
      • 1.2栈的实现
    • 2.队列
      • 2.1队列的概念与结构
      • 2.2队列的实现
  • 🥇结语

🎯引言

欢迎来到HanLop博客的C语言数据结构初阶系列。在之前的文章中,我们详细介绍了链表及其操作方法。在本篇文章中,我们将深入探讨栈和队列这两种常见的数据结构。栈和队列虽然都是线性数据结构,但它们在数据的存取方式上有着显著的区别。栈是一种后进先出(LIFO, Last In First Out)的数据结构,而队列则是一种先进先出(FIFO, First In First Out)的数据结构。通过理解和掌握这两种数据结构,您将能更有效地管理数据,并为后续更复杂的数据结构和算法的学习打下坚实的基础。让我们一起开始探索栈和队列的奥秘吧!

👓栈和队列

1.栈

1.1栈的概念与结构

栈(Stack)是一种特殊的线性数据结构,它遵循“后进先出”(LIFO, Last In First Out)的原则。这意味着在栈中,最后一个被插入的数据将是第一个被取出的数据。

栈顶和栈底

栈顶和栈底是栈结构中两个重要的概念:

  • 栈顶(Top): 栈顶是指栈中最后一个被插入的元素的位置。在执行压栈(Push)和出栈(Pop)操作时,都是针对栈顶进行的。因此,栈顶是栈中唯一可以进行插入和删除操作的地方。
  • 栈底(Bottom): 栈底是指栈中第一个被插入的元素的位置。在一个空栈中,栈底和栈顶的指针都是相同的。随着新的元素不断压入栈中,栈顶的位置会发生变化,但栈底的位置始终保持不变。

图示结构:

在这里插入图片描述

1.2栈的实现

栈的实现主要有两种方式:

  1. 数组实现: 使用数组来存储栈中的元素。用数组相较于用链表更优一些
  2. 链表实现: 使用链表来存储栈中的元素。

Stack.h源码

//Stack.h文件中
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>typedef int StackDataType;
typedef struct Stack
{StackDataType* arr;int top;int capacity;
}Stack;//初始栈
void StackInit(Stack* ps);//销毁栈
void StackDestory(Stack* ps);//入栈
void StackPush(Stack* ps, StackDataType	x);//判断栈是否为空
bool IsEmpty(Stack* ps);//取出栈顶元素
StackDataType StackTop(Stack* ps);//获取链表有效个数
int StackSize(Stack* ps);//出栈
void StackPop(Stack* ps);

Stack.c源码

#include "Stack.h"//初始栈
void StackInit(Stack* ps)
{assert(ps);ps->arr = NULL;ps->capacity = ps->top = 0;
}//入栈
void StackPush(Stack* ps, StackDataType	x)
{assert(ps);//检查容量if (ps->top == ps->capacity){int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;StackDataType* tmp = (StackDataType*)realloc(ps->arr,sizeof(StackDataType) * newcapacity);if (tmp != NULL){ps->arr = tmp;ps->capacity = newcapacity;}else{exit(1);}}ps->arr[ps->top] = x;ps->top++;
}
//判断栈是否为空
bool IsEmpty(Stack* ps)
{return ps->top == 0;
}//出栈
void StackPop(Stack* ps)
{assert(ps);assert(!IsEmpty(ps));ps->top--;
}//取出栈顶元素
StackDataType StackTop(Stack* ps)
{assert(ps);assert(!IsEmpty(ps));return ps->arr[ps->top - 1];
}//获取链表有效个数
int StackSize(Stack* ps)
{return ps->top;
}//销毁
void StackDestory(Stack* ps)
{assert(ps);free(ps->arr);ps->arr = NULL;ps->capacity = ps->top = 0;
}

函数实现详解:

1. 初始化栈 StackInit(Stack* ps)

void StackInit(Stack* ps)
{assert(ps);ps->arr = NULL;ps->capacity = ps->top = 0;
}

功能

初始化栈,确保栈的所有成员变量都有合理的初始值。

实现细节

  • 使用 assert(ps) 检查传入的栈指针 ps 是否为 NULL
  • 将栈的数组指针 arr 设为 NULL,表示栈中没有元素。
  • 将栈的容量 capacity 和栈顶指针 top 都设为 0,表示栈是空的。

2. 入栈 StackPush(Stack* ps, StackDataType x)

void StackPush(Stack* ps, StackDataType x)
{assert(ps);// 检查容量if (ps->top == ps->capacity){int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;StackDataType* tmp = (StackDataType*)realloc(ps->arr, sizeof(StackDataType) * newcapacity);if (tmp != NULL){ps->arr = tmp;ps->capacity = newcapacity;}else{exit(1);}}ps->arr[ps->top] = x;ps->top++;
}

功能

将一个元素压入栈顶,如果栈的容量不足,则动态扩展栈的容量。

实现细节

  • 使用 assert(ps) 检查传入的栈指针 ps 是否为 NULL
  • 检查栈的容量是否足够,如果栈顶指针 top 等于当前容量 capacity,表示容量已满。
  • 如果容量已满,计算新的容量:如果当前容量为 0,则设为 4,否则容量翻倍。
  • 使用 realloc 动态分配新的内存空间,并将原有元素复制到新空间。
  • 检查 realloc 是否成功,如果成功则更新栈的数组指针 arr 和容量 capacity,否则退出程序。
  • 将新元素放入栈顶位置,并更新栈顶指针 top

3. 判断栈是否为空 IsEmpty(Stack* ps)

bool IsEmpty(Stack* ps)
{return ps->top == 0;
}

功能

检查栈是否为空。

实现细节

  • 检查栈顶指针 top 是否为 0,如果是,则表示栈为空,返回 true,否则返回 false

4. 出栈 StackPop(Stack* ps)

void StackPop(Stack* ps)
{assert(ps);assert(!IsEmpty(ps));ps->top--;
}

功能

从栈顶移除一个元素。

实现细节

  • 使用 assert(ps) 检查传入的栈指针 ps 是否为 NULL
  • 使用 assert(!IsEmpty(ps)) 确保栈不为空,否则操作无效。
  • 将栈顶指针 top 减一,表示移除了栈顶元素。

5. 取出栈顶元素 StackTop(Stack* ps)

StackDataType StackTop(Stack* ps)
{assert(ps);assert(!IsEmpty(ps));return ps->arr[ps->top - 1];
}

功能

返回栈顶元素的值,但不移除该元素。

实现细节

  • 使用 assert(ps) 检查传入的栈指针 ps 是否为 NULL
  • 使用 assert(!IsEmpty(ps)) 确保栈不为空,否则操作无效。
  • 返回栈顶元素,即数组中索引为 top - 1 位置的元素。

6. 获取栈中有效元素个数 StackSize(Stack* ps)

int StackSize(Stack* ps)
{return ps->top;
}

功能

返回栈中当前元素的个数。

实现细节

  • 直接返回栈顶指针 top 的值,因为 top 指示了栈中元素的个数。

7. 销毁栈 StackDestory(Stack* ps)

void StackDestory(Stack* ps)
{assert(ps);free(ps->arr);ps->arr = NULL;ps->capacity = ps->top = 0;
}

功能

释放栈的内存,并重置栈的所有成员变量。

实现细节

  • 使用 assert(ps) 检查传入的栈指针 ps 是否为 NULL
  • 使用 free 释放栈的数组指针 arr 指向的内存。
  • arr 设为 NULL,防止悬空指针。
  • 将栈的容量 capacity 和栈顶指针 top 都设为 0,重置栈。

2.队列

2.1队列的概念与结构

队列(Queue)是一种线性数据结构,它遵循“先进先出”(FIFO, First In First Out)的原则。队列中的元素总是从队尾插入,从队头删除。换句话说,第一个插入的元素也是第一个被删除的元素。

队头和队尾

  • 队头(Front): 队头是指队列中最早插入的元素所在的位置。在执行出队(Dequeue)操作时,元素从队头移除。
  • 队尾(Rear): 队尾是指队列中最后插入的元素所在的位置。在执行入队(Enqueue)操作时,元素插入到队尾。

图示:

在这里插入图片描述

2.2队列的实现

队列的实现主要有两种方式:

  1. 数组实现: 使用数组来存储队列中的元素。出队的时候要移动数据,时间复杂度较高不建议使用
  2. 链表实现: 使用链表来存储队列中的元素。出队时间复杂度O(1),下面将会用链表的方式实现队列

Queue.h源码

//Queue.h文件中
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int QueueDataType;
typedef struct QueueNode
{QueueDataType x;struct QueueNode* next;
}QueueNode;
typedef struct Queue
{QueueNode* phead;QueueNode* ptail;int size;
}Queue;//初始化队列
void QueueInit(Queue* pq);//队列判空
bool QueueEmpty(Queue* pq);//入队列,队尾
void QueuePush(Queue* pq, QueueDataType x);//出队列,队头
void QueuePop(Queue* pq);//取队头数据
QueueDataType QueueFront(Queue* pq);//取队尾数据
QueueDataType QueueBack(Queue* pq);//队列的有效个数
int QueueSize(Queue* pq);

定义数据类型

typedef int QueueDataType;

功能: 定义队列数据类型。
说明: 使用 typedefint 类型定义为 QueueDataType,这样如果以后需要更改队列存储的数据类型,只需要修改这一行即可。

定义队列节点结构体

typedef struct QueueNode
{QueueDataType x;struct QueueNode* next;
} QueueNode;

功能: 定义队列节点结构体。
说明:

  • QueueDataType x:存储节点数据。
  • struct QueueNode* next:指向下一个节点的指针。

定义队列结构体

typedef struct Queue
{QueueNode* phead;QueueNode* ptail;int size;
} Queue;

功能: 定义队列结构体。
说明:

  • QueueNode* phead:指向队列头部节点的指针。
  • QueueNode* ptail:指向队列尾部节点的指针。
  • int size:记录队列中元素的个数。

Queue.c源码

//Queue.c文件中
#include "Queue.h"//初始化队列
void QueueInit(Queue* pq)
{assert(pq);pq->size = 0;pq->phead = pq->ptail = NULL;}//队列判空
bool QueueEmpty(Queue* pq)
{return pq->phead == NULL;
}//入队列,队尾
void QueuePush(Queue* pq, QueueDataType x)
{assert(pq);QueueNode* newNode = (QueueNode*)malloc(sizeof(QueueNode));newNode->next = NULL;newNode->x = x;if (newNode == NULL){perror("malloc fail");exit(1);}if ((pq->phead == NULL)&&(pq->ptail==NULL)){pq->phead = pq->ptail = newNode;}else{pq->ptail->next = newNode;pq->ptail = pq->ptail->next;}pq->size++;
}//出队列,队头
void QueuePop(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));QueueNode* del = pq->phead;if (pq->phead == pq->ptail){pq->phead = pq->ptail = NULL;free(del);del = NULL;}else{pq->phead = pq->phead->next;free(del);del = NULL;}pq->size--;
}//取队头数据
QueueDataType QueueFront(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->phead->x;
}//取队尾数据
QueueDataType QueueBack(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->ptail->x;
}//队列的有效个数
int QueueSize(Queue* pq)
{assert(pq);return pq->size;
}

函数实现详解

1. QueueInit

void QueueInit(Queue* pq)
{assert(pq);pq->size = 0;pq->phead = pq->ptail = NULL;
}

功能: 初始化队列,将队列的头指针和尾指针都设为 NULL,并将队列大小设为 0。
参数: Queue* pq:指向队列结构体的指针。
返回值: 无。

实现过程:

  • 使用 assert 确保传入的指针不为空。
  • 将队列的头指针和尾指针都设为 NULL
  • 将队列的大小设为 0。

2. QueueEmpty

bool QueueEmpty(Queue* pq)
{return pq->phead == NULL;
}

功能: 检查队列是否为空。
参数: Queue* pq:指向队列结构体的指针。
返回值: bool:如果队列为空,返回 true;否则返回 false

实现过程:

  • 检查队列的头指针是否为 NULL。如果是,则队列为空,返回 true;否则返回 false

3. QueuePush

void QueuePush(Queue* pq, QueueDataType x)
{assert(pq);QueueNode* newNode = (QueueNode*)malloc(sizeof(QueueNode));newNode->next = NULL;newNode->x = x;if (newNode == NULL){perror("malloc fail");exit(1);}if ((pq->phead == NULL) && (pq->ptail == NULL)){pq->phead = pq->ptail = newNode;}else{pq->ptail->next = newNode;pq->ptail = pq->ptail->next;}pq->size++;
}

功能: 将新元素插入到队列的尾部。
参数: Queue* pq:指向队列结构体的指针;QueueDataType x:要插入的元素。
返回值: 无。

实现过程:

  • 使用 assert 确保传入的指针不为空。
  • 使用 malloc 分配一个新的节点,并检查内存分配是否成功。如果分配失败,打印错误信息并退出程序。
  • 将新节点的 next 指针设为 NULL,并将数据 x 存储在新节点中。
  • 检查队列是否为空(头指针和尾指针都为 NULL),如果为空,将新节点设为头节点和尾节点。
  • 如果队列不为空,将新节点添加到队列尾部,并更新尾指针。
  • 增加队列的大小。

4. QueuePop

void QueuePop(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));QueueNode* del = pq->phead;if (pq->phead == pq->ptail){pq->phead = pq->ptail = NULL;}else{pq->phead = pq->phead->next;}free(del);pq->size--;
}

功能: 删除队列头部的元素。
参数: Queue* pq:指向队列结构体的指针。
返回值: 无。

实现过程:

  • 使用 assert 确保传入的指针不为空且队列不为空。
  • 保存头节点的指针。
  • 如果头节点和尾节点相同,将头尾指针都设为 NULL
  • 否则,将头指针指向下一个节点。
  • 释放头节点的内存,并减少队列大小。

5. QueueFront

QueueDataType QueueFront(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->phead->x;
}

功能: 获取队列头部的元素,但不删除。
参数: Queue* pq:指向队列结构体的指针。
返回值: QueueDataType:队列头部的元素。

实现过程:

  • 使用 assert 确保传入的指针不为空且队列不为空。
  • 返回头节点的数据。

6. QueueBack

QueueDataType QueueBack(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->ptail->x;
}

功能: 获取队列尾部的元素,但不删除。
参数: Queue* pq:指向队列结构体的指针。
返回值: QueueDataType:队列尾部的元素。

实现过程:

  • 使用 assert 确保传入的指针不为空且队列不为空。
  • 返回尾节点的数据。

7. QueueSize

int QueueSize(Queue* pq)
{assert(pq);return pq->size;
}

功能: 获取队列中的元素个数。
参数: Queue* pq:指向队列结构体的指针。
返回值: int:队列的大小。

实现过程:

  • 使用 assert 确保传入的指针不为空。
  • 返回队列的大小。

🥇结语

通过本篇文章的学习,我们了解了栈和队列的基本概念、操作方法及其应用场景。栈作为后进先出的数据结构,常用于递归算法和回溯问题的解决,而队列作为先进先出的数据结构,则在任务调度和广度优先搜索中有着重要应用。希望通过本文的讲解,您能掌握栈和队列的使用技巧,并在实际编程中灵活应用。下一篇文章,我们将继续探讨更为复杂的数据结构,敬请期待!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/47607.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

8年前端总结和感想(转)~

我是牛奶&#xff0c;本文是我前端工作 8 年的一些总结和感想 主要记录下个人点滴、前端知识点、场景应用、未来的憧憬以及个人规划&#xff0c;供自己以后查漏补缺&#xff0c;也欢迎同道朋友交流学习。 自我介绍 我是一名工作在非知名公司的 8 年前端&#xff0c;双非普通本…

JMeter使用手册

安装 下载地址 https://jmeter.apache.org/download_jmeter.cgi 下载后解压到win的文件夹中 打开JMeter的bin文件夹&#xff0c;双击这个jar就启动了JMeter 启动 出现这样的界面 基本使用 添加变量 这个变量在使用的时候可以被引用 创建线程组 所有的请求都得基于…

Linux:Linux进程概念

目录 前言 1. 冯诺依曼体系结构 2. 操作系统 2.1 什么是操作系统 3. 进程 3.1 基本概念 3.2 描述进程——PCB 3.3 进程和程序的区别 3.4 task_struct-PCB的一种 3.5 task_struct的内容分类 4. 查看进程 4.1 通过系统文件查看进程 4.2 通过ps指令查看进程 4.3 …

lse:一款专为渗透测试和CTF设计的Linux枚举工具

关于linux-smart-enumeration linux-smart-enumeration是一款专为渗透测试和CTF设计的Linux枚举工具&#xff0c;该工具可以帮助广大研究人员收集与本地Linux系统安全相关的信息。 工具特性 该工具从2.0版本开始符合POSIX标准&#xff0c;并且经过了shellcheck和posh测试。它…

前端三大主流框架Vue React Angular有何不同?

前端主流框架&#xff0c;Vue React Angular&#xff0c;大家可能都经常在使用&#xff0c;Vue React&#xff0c;国内用的较多&#xff0c;Angualr相对用的少一点。但是大家有思考过这三大框架的不同吗&#xff1f; 一、项目的选型上 中小型项目&#xff1a;Vue2、React居多…

【数据结构-前缀和】力扣2550.统计范围内的元音字符串数

给你一个下标从 0 开始的字符串数组 words 以及一个二维整数数组 queries 。 每个查询 queries[i] [li, ri] 会要求我们统计在 words 中下标在 li 到 ri 范围内&#xff08;包含 这两个值&#xff09;并且以元音开头和结尾的字符串的数目。 返回一个整数数组&#xff0c;其中…

中文诗歌生成

用transformer在诗歌集上训练出的模型 import os os.environ["KERAS_BACKEND"] "tensorflow" # param ["tensorflow", "jax", "torch"] os.environ[TF_CPP_MIN_LOG_LEVEL] 2 os.environ[HF_ENDPOINT] https://hf-mirro…

IT程序员的黄金机遇

在这个数字化时代&#xff0c;IT程序员不仅是技术革新的推动者&#xff0c;更是全球经济的重要支柱。而对于拥有一技之长的IT人才来说&#xff0c;加拿大正敞开大门&#xff0c;提供一片充满机遇的热土。本文将为你揭示为何加拿大是IT程序员移民的不二之选&#xff0c;并提供实…

SecureCRT (mac or windows)解决中文显示乱码

中文乱码问题的方法主要包括设置SecureCRT的编码为UTF-8以及设置LANG环境变量为zh_CN.UTF-8。‌ 1.设置SecureCRT的编码为UTF-8&#xff1a;‌ 打开SecureCRT&#xff0c;‌进入Options -> Global Options -> Default Session -> Edit Default Settings-> Appear…

深入理解设计模式:六大经典模式解析

深入理解设计模式&#xff1a;六大经典模式解析 1. 单例模式&#xff08;Singleton Pattern&#xff09;1.1 概述1.2 示例场景1.3 实现要点 2. 工厂模式&#xff08;Factory Pattern&#xff09;2.1 简单工厂2.2 抽象工厂2.3 示例场景2.4 实现要点 3. 观察者模式&#xff08;Ob…

Idea配置远程开发

Idea配置远程开发 本篇博客介绍使用idea通过ssh连接ubuntu服务器进行开发 目录 Idea配置远程开发1.idae上点击file->Remote Development2.点击New Connection3.填写相关信息4.输入密码5.选择IDE版本和项目路径5.1 点击open an SSH terminal打开控制台5.2 依次执行命令 6.成…

竖版html网页简易抽奖系统

<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>在线抽奖 随机选取 自动挑选</title> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <script src"htt…

初阶数据结构的实现1 顺序表和链表

顺序表和链表 1.线性表1.1顺序表1.1.1静态顺序表&#xff08;不去实现&#xff09;1.1.2动态顺序表1.1.2.1 定义程序目标1.1.2.2 设计程序1.1.2.3编写代码1.1.2.3测试和调试代码 1.1.2 顺序表的问题与思考 1.2链表1.2.1链表的概念及结构1.2.1.1 定义程序目标1.2.1.2 设计程序1.…

人工智能算法工程师(高级)课程4-图像生成项目之自编码生成模型与代码详解

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能算法工程师(高级)课程4-图像生成项目之自编码生成模型与代码详解。自编码生成模型&#xff08;Autoencoder&#xff09;是一种无监督学习算法&#xff0c;旨在通过编码器和解码器学习数据的有效表示。本文将…

聊聊Hugging Face

概述 HuggingFace是一个开源社区&#xff0c;提供了开源的AI研发框架、工具集、可在线加载的数据集仓库和预训练模型仓库。HuggingFace提出了一套可以依照的标准研发流程&#xff0c;按照该框架实施工程&#xff0c;能够在一定程度上规避开发混乱、开发人员水平不一致的问题&a…

spring 5.3.x 、6.1.x、6.0.x 源码本地编译运行

参考大佬文章&#xff0c;完美完成本地idea spring源码编译和demo测试 参考链接&#xff08;spring5.3.x&#xff09; spring5.3.x源码阅读环境搭建 下面是spring6.0.x参考 spring6.0.x jdk调成17 idea 2022.2.4版本本地编译spring源码项目 spring6.0.x 分支 gradle-8…

ubuntu22.04 配置grpc(优化官方教程)

优化了官方教程&#xff0c;2024.7.17顺利打通。 一&#xff1a;添加环境变量 打开root文件夹下的 .bashrc 文件 编辑文件&#xff1a;滚动到文件的底部&#xff0c;然后添加以下行&#xff1a; export MY_INSTALL_DIR$HOME/.local mkdir -p "$MY_INSTALL_DIR" exp…

视觉巡线小车——STM32+OpenMV(三)

目录 前言 一、OpenMV代码 二、STM32端接收数据 1.配置串口 2.接收数据并解析 总结 前言 通过视觉巡线小车——STM32OpenMV&#xff08;二&#xff09;&#xff0c;已基本实现了减速电机的速度闭环控制。要使小车能够自主巡线&#xff0c;除了能够精准的控制速度之外&#xff0…

Hadoop3:MR程序处理小文件的优化办法(uber模式)

一、解决方案 1、在数据采集的时候&#xff0c;就将小文件或小批数据合成大文件再上传HDFS&#xff08;数据源头&#xff09; 2、Hadoop Archive&#xff08;存储方向&#xff09; 是一个高效的将小文件放入HDFS块中的文件存档工具&#xff0c;能够将多个小文件打包成一个HAR…

深入理解 Linux Zero-copy 原理与实现策略图解

用户态和内核态 一般来说&#xff0c;我们在编写程序操作 Linux I/O 之时十有八九是在用户空间和内核空间之间传输数据&#xff0c;因此有必要先了解一下 Linux 的用户态和内核态的概念。 从宏观上来看&#xff0c;Linux 操作系统的体系架构分为用户态和内核态&#xff08;或者…