栈和队列(C 语言)

目录

  • 一、栈
    • 1. 栈的概念
    • 2. 栈的结构
    • 3. 栈的实现思路
    • 4. 栈的实现代码
  • 二、队列
    • 1. 队列的概念
    • 2. 队列的结构
    • 3. 队列的实现思路
    • 4. 队列的实现代码
    • 5. 循环队列

一、栈

1. 栈的概念

栈是一种特殊的线性表,只允许在固定的一端进行插入和删除操作,该端被称为栈顶,相对的另一端被称为栈底。栈中的元素遵循后进先出的规则LIFO(Last In First Out)。就像一堆书,规定一次只能拿一本或者放一本,那么最先放的书在最下面,最后放的书在最上面。

2. 栈的结构

在这里插入图片描述

3. 栈的实现思路

栈可以通过数组来实现,也可以通过链表来实现。

(1)数组
在这里插入图片描述

(2)链表
在这里插入图片描述

可以看到,在栈顶的插入和删除方面,数组是尾插和尾删,链表是头插和头删,时间复杂度都是O(1)。但是链表每次都需要申请节点,而数组扩容一次就可以使用一段时间,所以相对来说数组更优一点。

栈需要以下几种功能:
(1)在栈顶插入和删除
(2)返回栈顶元素
(3)检查栈是否为空
(4)返回当前栈中有效元素个数

所以,该栈结构需要三个成员:指向数据的指针,栈顶位置的记录,栈当前的容量大小。

4. 栈的实现代码

老样子,三个文件:头文件,方法实现文件,测试文件。

头文件:Stack.h

// 头文件
#include <stdio.h>
#include <stdbool.h>// 栈// 类型声明
typedef int DataType;// 常量声明
#define CAPACITY_INIT 8// 栈结构声明
typedef struct Stack
{DataType* pdata;  // 指向数据的指针size_t top;  // 下一个入栈位置size_t capacity;  // 当前栈的容量
}Stack;// 方法// 初始化栈
void StackInit(Stack* sk);// 入栈
void StackPush(Stack* sk, DataType data);// 出栈
void StackPop(Stack* sk);// 栈空判断
bool StackEmpty(const Stack* sk);// 返回栈中当前元素个数
size_t StackSize(const Stack* sk);// 返回栈顶元素
DataType StackTop(const Stack* sk);// 销毁栈
void StackDestory(Stack* sk);

方法实现文件:Stack.c

// 头文件
#include "Stack.h"
#include <assert.h>
#include <stdlib.h>// 方法// 初始化栈
void StackInit(Stack* sk)
{assert(sk);// 申请空间sk->pdata = (DataType*)malloc(sizeof(DataType) * CAPACITY_INIT);if (NULL == sk->pdata){perror("StackInit::malloc: ");exit(-1);}// 赋值sk->capacity = CAPACITY_INIT;sk->top = 0;
}// 入栈
void StackPush(Stack* sk, DataType data)
{assert(sk);// 增容判断if (sk->top == sk->capacity){// 增容DataType* tmp = (DataType*)realloc(sk->pdata, sizeof(DataType) * sk->capacity * 2);if (NULL == tmp){perror("StackPush::realloc: ");exit(-1);}// 成功后更新sk->pdata = tmp;sk->capacity *= 2;}// 入栈sk->pdata[sk->top++] = data;
}// 出栈
void StackPop(Stack* sk)
{// 空栈判段if (0 == sk->top){printf("Empty Stack!\n");exit(-1);}// 出栈--sk->top;
}// 栈空判断
bool StackEmpty(const Stack* sk)
{assert(sk);return (0 == sk->top);
}// 返回栈中当前元素个数
size_t StackSize(const Stack* sk)
{assert(sk);return sk->top;
}// 返回栈顶元素
DataType StackTop(const Stack* sk)
{assert(sk);// 空栈判断if (0 == sk->top){printf("Empty Stack!\n");exit(-1);}// 返回return sk->pdata[sk->top - 1];
}// 销毁栈
void StackDestory(Stack* sk)
{assert(sk);// 释放数据free(sk->pdata);sk->pdata = NULL;// 成员置零sk->top = sk->capacity = 0;
}

测试文件:test.c

// 头文件
#include "Stack.h"int main()
{// 栈测试// 创建栈Stack sk;// 初始化栈StackInit(&sk);// 入栈int i = 0;for (i = 1; i <= 9; ++i){StackPush(&sk, i);}// 出栈+显示for (i = 0; i < 9; ++i){printf("%d ", StackTop(&sk));StackPop(&sk);}printf("\n");// 销毁栈StackDestory(&sk);
}

(4)测试结果:
在这里插入图片描述

也可以使用链表来实现栈,但是现在的栈都是使用数组来实现,相关的面试题给的栈也是用数组实现的。感兴趣的读者可以自己尝试使用链表实现一个栈。

二、队列

1. 队列的概念

队列也是一种特殊的线性表,它只能从前端出数据,从后端入数据,符合先进先出的规则FIFO(First In First Out)。也就和我们日常排队是一个道理,当然插队不算数哈。

2. 队列的结构

在这里插入图片描述

3. 队列的实现思路

队列通常使用链表来实现,为什么不用数组?因为数组头插需要挪动数据,有人说可以在队头出数据,在队尾入数据,那扩容之后怎么办?那链表在尾部入数据还要找尾呢,这个可以在队列结构中添加一个尾指针就可以解决。

所以,队列包含两个链表节点指针,一个指向队列的队头,另一个指向队列的队尾。

队列需要实现如下功能:
(1)在队头出数据
(2)在队尾入数据
(3)空队检查
(4)返回当前队列元素个数
(5)返回队头元素
(6)返回队尾元素

为了方便返回队列当前元素个数,可以在队列结构中添加一个 size 变量,专门记录当前元素个数。

4. 队列的实现代码

头文件:Queue.h

// 头文件
#include <stdio.h>
#include <stdbool.h>// 队列// 类型声明
typedef int DataType;// 节点结构声明
typedef struct Node
{DataType val;struct Node* next;
}Node;// 队列结构声明
typedef struct Queue
{Node* head;Node* tail;size_t size;
}Queue;// 方法// 初始化队列
void QueueInit(Queue* q);// 入队
void QueuePush(Queue* q, DataType data);// 出队
void QueuePop(Queue* q);// 返回队头元素
DataType QueueFront(Queue* q);// 返回队尾元素
DataType QueueBack(Queue* q);// 空队判断
bool QueueEmpty(Queue* q);// 返回队列当前元素个数
size_t QueueSize(Queue* q);// 销毁队列
void QueueDestory(Queue* q);

方法实现文件:Queue.c

// 头文件
#include "Queue.h"
#include <assert.h>
#include <stdlib.h>// 方法// 初始化队列
void QueueInit(Queue* q)
{assert(q);// 初始化q->head = q->tail = NULL;q->size = 0;
}// 入队
void QueuePush(Queue* q, DataType data)
{assert(q);// 申请新节点Node* new_node = (Node*)malloc(sizeof(Node));if (NULL == new_node){perror("QueuePush::malloc: ");exit(-1);}// 申请成功,赋值new_node->val = data;new_node->next = NULL;// 链接入队// 头插判断if (NULL == q->head){q->head = q->tail = new_node;}else  // 尾插{q->tail->next = new_node;q->tail = new_node;}// 元素个数 +1++q->size;
}// 出队
void QueuePop(Queue* q)
{assert(q);// 空队判断if (NULL == q->head){printf("Empty Queue!\n");exit(-1);}// 出队// 尾删判断if (NULL == q->head->next){// 释放free(q->head);// 置空q->head = q->tail = NULL;}else{// 存储下一个结点Node* next = q->head->next;// 删除头节点free(q->head);// 新头节点q->head = next;}// 元素个数 -1--q->size;
}// 返回队头元素
DataType QueueFront(Queue* q)
{assert(q);// 空队判断if (NULL == q->head){printf("Empty Queue!\n");exit(-1);}// 返回return q->head->val;
}// 返回队尾元素
DataType QueueBack(Queue* q)
{assert(q);// 空队判断if (NULL == q->head){printf("Empty Queue!\n");exit(-1);}// 返回return q->tail->val;
}// 空队判断
bool QueueEmpty(Queue* q)
{assert(q);return (0 == q->size);
}// 返回队列当前元素个数
size_t QueueSize(Queue* q)
{assert(q);return q->size;
}// 销毁队列
void QueueDestory(Queue* q)
{assert(q);Node* cur = q->head;while (cur){// 存储下一个节点Node* next = cur->next;// 释放当前节点free(cur);// 下一个节点cur = next;}// 成员置空q->head = q->tail = NULL;q->size = 0;
}

测试文件:test.c

// 头文件
#include "Queue.h"int main()
{// 队列测试// 创建队列Queue q;// 初始化队列QueueInit(&q);// 入队int i = 0;for (i = 1; i <= 9; ++i){QueuePush(&q, i);}// 出队+打印for (i = 0; i < 9; ++i){printf("%d ", QueueFront(&q));QueuePop(&q);}// 销毁队列QueueDestory(&q);return 0;
}

5. 循环队列

在实际中,我们有时还会用到一种队列叫循环队列。如:打印机任务队列、缓存管理等。环形队列可以使用数组实现,也可以使用链表实现。

其结构如下:
在这里插入图片描述

虽然上述图片看着很容易实现,但是还是存在一定的问题。比如使用数组来实现,那么定义两个下标变量——head 和 tail,分别表示队头和队尾,但是你会发现,队列为空时,两个下标相等;队列已满时,两个下标还是相等。那么如何进行区分?

解决办法:
(1)添加一个 size_t 类型的变量 size 用来表示当前循环队列中的元素个数,那么当两个下标相同时,通过该 size 是否为 0 就可以判断出当前是空队还是满队。

(2)申请空间时多申请一个,该空间不使用,仅用来占位。那么当 head == tail 时,循环队列为空;当 head == tail+1 时循环队列已满。

上面只是实现循环的思路,具体实现的时候还是要具体分析。本篇不给出循环队列的代码实现,因为接着的相关面试题中有一道设计一个循环队列,在那篇博客中作者会给出上述两种实现方法。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

自动化测试工具Ranorex Studio(二十五)-库的拆分

默认地&#xff0c;每一个Ranorex Studio项目包含一个对象库文件&#xff0c;这个文件自动用在每一个新创建的录制中。你可以在一个单独的库文件中管理一个测试套件项目中所有的UI元素&#xff0c;但是在一个自动化测试项目中多个对象库的存在还是有一些原因的&#xff1a; .测…

Centos下安装Maven(无坑版)

Linux 安装 Maven Maven 压缩包下载与解压 华为云下载源&#xff0c;自行选择版本 下面的示例使用的是 3.8.1 版本 wget https://repo.huaweicloud.com/apache/maven/maven-3/3.8.1/binaries/apache-maven-3.8.1-bin.tar.gz解压 tar -zxvf apache-maven-3.8.1-bin.tar.gz移…

99、Python并发编程:多线程的问题、临界资源以及同步机制

引言 多线程技术的引入&#xff0c;可以帮助我们实现并发编程&#xff0c;一方面可以充分利用CPU计算资源&#xff0c;另一方面&#xff0c;可以在用户体验上带来极大的改善。但是&#xff0c;多线程技术也存在一些问题。本文就来简单聊一下多线程引入导致的问题&#xff0c;以…

jmeter常用配置元件介绍总结之取样器

系列文章目录 1.windows、linux安装jmeter及设置中文显示 2.jmeter常用配置元件介绍总结之安装插件 3.jmeter常用配置元件介绍总结之取样器 jmeter常用配置元件介绍总结之取样器 2.取样器2.1.HTTP请求2.2.Debug Sampler2.3.JSR223 Sampler2.4.JDBC Connection Configuration和J…

Python练习11

Python日常练习 题目&#xff1a; 编写一个石头剪刀布游戏&#xff0c;该程序要求完成如下功能&#xff1a; (1) 显示游戏规则&#xff0c;提醒用户输入一个1-3的整数或者直接回车。 用户输入回车时游戏结束。 用户输入不合法&#xff08;包括输入的…

什么是欧拉角和四元数

涉及机器人调度工作的一些基本概念整理理解 目录 什么是欧拉角和四元数 &#xff1f;相关工具网站相关工具代码 什么是欧拉角和四元数 &#xff1f; 这里画了一张图&#xff0c;简明方便理解&#xff1a; 欧拉角 (Euler Angles) 是一种描述物体在三维空间旋转姿态的方法&…

关于几种卷积

1*1卷积 分组卷积&深度可分离卷积 空洞卷积、膨胀卷积 转置卷积 https://zhuanlan.zhihu.com/p/80041030 https://yinguobing.com/separable-convolution/#fn2 11的卷积可以理解为对通道进行加权&#xff0c;对于一个通道来说&#xff0c;每个像素点加权是一样的&am…

std::copy

std::copy 是 C 标准库中的一个算法&#xff0c;用于将一个序列中的元素复制到另一个位置。这个算法定义在 <algorithm> 头文件中。 --- 函数原型 std::copy 有几个不同的重载版本&#xff0c;但以下是最常用的两个&#xff1a; template <class InputIterator, c…

PyQt5实战——翻译的实现,第一次爬取微软翻译经验总结(八)

个人博客&#xff1a;苏三有春的博客 系类往期文章&#xff1a; PyQt5实战——多脚本集合包&#xff0c;前言与环境配置&#xff08;一&#xff09; PyQt5实战——多脚本集合包&#xff0c;UI以及工程布局&#xff08;二&#xff09; PyQt5实战——多脚本集合包&#xff0c;程序…

【数据集】【YOLO】【VOC】目标检测数据集,查找数据集,yolo目标检测算法详细实战训练步骤!

数据集列表 帮忙采集开源数据集&#xff0c;包括YOLO格式数据集和Pascal VOC格式数据集&#xff0c;含图像原文件和标注文件&#xff0c;几百张到几千张不等&#xff0c;国内外公开数据集均可。 针对目标检测&#xff0c;YOLO系列模型训练&#xff0c;分类训练等。 部分数据…

Vue前端开发:元素动画效果之过渡动画

在Vue中&#xff0c;专门提供了一个名称为transition 的内置组件&#xff0c;来完成单个DOM元素的动画效果&#xff0c;该组件本身和它的顶层并不渲染动画效果&#xff0c;而只是将动画效果应用到被组件包裹的DOM元素上&#xff0c;代码实现的格式如下所示。 <transition&g…

【C/C++】strcmp函数的模拟实现

零.导言 之前我们学习了strcmp函数&#xff0c;不妨我们现在尝试模拟实现strcmp函数的功能。 一.实现strcmp函数的要点 strcmp函数是一种字符串函数&#xff0c;可以比较字符类型的数组&#xff0c;因此我们自定义的模拟函数需要两个char类型的指针参数&#xff1b;第一个字符…

ima.copilot:智慧因你而生

在数字化时代&#xff0c;信息的获取、处理和创作已经成为我们日常工作和学习中不可或缺的一部分。腾讯公司推出的ima.copilot&#xff08;简称ima&#xff09;正是为了满足这一需求&#xff0c;它是一款由腾讯混元大模型提供技术支持的智能工作台产品&#xff0c;旨在通过智能…

string类

1. 标准库中的string类 1.1 string类(了解) string - C Reference 在使用string类时&#xff0c;必须包含 # include头文件以及 using namespace std; 1.2 auto和范围for 1&#xff09;auto关键字 作为一个新的类型指示符来指示编译器&#xff0c;auto声明的变量必须由编…

元数据管理是如何在ETL过程中发挥作用的?

ETL&#xff08;抽取、转换和加载&#xff09;技术在现代大数据处理中起着至关重要的作用。ETL技术主要用于将不同来源、格式和结构的数据抽取到一个中心化的数据仓库&#xff0c;并进行转换和加载&#xff0c;进而提供一致、高质量的数据给数据分析和报告工具。然而&#xff0…

vscode Comment Translate 反应慢 加载中...

Comment Translate 版本&#xff1a;v2.3.3 你是不是疑惑切换了 Bing 源也无法使用还是加载中… 那么可能你切换Bing后没重启vscode 下面是切换成功后的插件日志&#xff0c;一定要重启vscode&#xff0c;只是禁用和启用插件不行的&#xff0c;另外google是没用的&#xff0c;用…

机器学习是什么?AIGC又是什么?机器学习与AIGC未来科技的双引擎

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…

如何搭建 ELK【elasticsearch+logstash+kibana】日志分析系统

一、为什么需要日志分析系统&#xff1f; 日志主要包括系统日志、应用程序日志和安全日志。系统运维和开发人员可以通过日志了解服务器软硬件信息、检查配置过程中的错误及错误发生的原因。经常分析日志可以了解服务器的负荷&#xff0c;性能安全性&#xff0c;从而及时采取措…

Android智能座驾,carlink场景截屏黑屏问题

背景 项目开发过程中&#xff0c;遇到如下问题&#xff1a; 【操作步骤】 1、建立导航音乐分屏 2、连接Carlink&#xff0c;车机端打开任意Carlink应用&#xff0c;点击音乐图标回到分屏 【结果】 页面会出现1s黑屏再显示分屏的情况 详细分析 比较怀疑是截屏的方法拿到的图片就…