顺序栈与链式栈

目录

1.  栈

1.1 栈的概念

2.  栈的实现

3. 顺序栈的实现

3.1 顺序栈的声明

3.2 顺序栈的初始化

3.3 顺序栈的入栈

 3.4 顺序栈的出栈

3.5 顺序栈获取栈顶元素

3.6 顺序栈获取栈内有效数据个数

3.7 顺序栈判断栈是否为空

3.8 顺序栈打印栈内元素

3.9 顺序栈销毁栈

3.10 完整代码

4. 链式栈的实现

4.1 链式栈的声明

4.2 链式栈的初始化

4.3 链式栈的入栈

4.4 链式栈的出栈

4.5 链式栈获取栈顶元素

4.6 链式栈获取栈内有效数据个数

4.7 链式栈判断栈是否为空

4.8 链式栈打印栈内元素

4.9 链式栈销毁栈

5. 疑问


1.  栈

1.1 栈的概念

栈:是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素的操作。进行数据插入和删除操作的一段称为栈顶,另一端称为栈底。栈中的元素遵守后进先出LIFO(Last In First Out)的原则。

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。

出栈:栈的删除操作叫做出栈。出数据也在栈顶。

2.  栈的实现

我们了解了栈的概念,我们就可以发现,栈其实可以用顺序表和链表来实现,我们根据它的实现方式,可以把它分为顺序栈与链式栈。

3. 顺序栈的实现

Stack.h

//需要用到的库函数的头文件
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int STDataType;typedef struct Stack
{STDataType* arr;//指向动态开辟的一块空间int top;//指向栈顶元素的下一个位置int capacity;//栈的容量
}st;//初始化栈
void STInit(st* pst);
//入栈
void STPush(st* pst,STDataType x);
//出栈
void STPop(st* pst);
//获取栈顶元素
STDataType STTop(st* pst);
//获取栈内有效数据个数
int STSize(st* pst);
//判断栈是否为空
bool STEmpty(st* pst);
//打印栈内元素
void STPrint(st* pst);
//销毁栈
void STDestroy(st* pst);

3.1 顺序栈的声明

Stack.h

typedef int STDataType;typedef struct Stack
{STDataType* arr;//指向动态开辟的一块空间int top;//指向栈顶元素的下一个位置int capacity;//栈的容量
}st;

这里我们在声明顺序栈的结构的时候,这个top可以是指向栈顶元素的,那我们在进行栈的初始化就需要将他初始化为-1,如果top是指向栈顶元素的下一个,那我们就需要将他的初始化设置为0。两种方法均可行,这里可以根据自己的习惯进行设置。

3.2 顺序栈的初始化

Stack.c

void STInit(st* pst)
{//判断pst是否为空指针assert(pst);pst->arr = NULL;pst->capacity = pst->top = 0;
}

3.3 顺序栈的入栈

Stack.c

void STPush(st* pst, STDataType x)
{//判断pst是否为空指针assert(pst);//检查空间大小,是否需要增容if (pst->capacity == pst->top){int newcapacity = pst->capacity == 0 ? 4 :2 * pst->capacity;STDataType* tmp = (STDataType*)realloc(pst->arr,newcapacity * sizeof(STDataType));if (tmp == NULL){perror("realloc");exit(1);}pst->arr = tmp;pst->capacity = newcapacity;}//入数据pst->arr[pst->top++] = x;
}

 3.4 顺序栈的出栈

Stack.c

void STPop(st* pst)
{//判断pst是否为空指针assert(pst);//判断栈是否为空assert(pst->top > 0);pst->top--;
}

3.5 顺序栈获取栈顶元素

Stack.c

STDataType STTop(st* pst)
{//判断pst是否为空指针assert(pst);//判断栈是否为空assert(pst->top > 0);//这里top指向栈顶元素的下一个位置,-1刚好指向栈顶元素return pst->arr[pst->top - 1];
}

3.6 顺序栈获取栈内有效数据个数

Stack.c

int STSize(st* pst)
{assert(pst);return pst->top;
}

3.7 顺序栈判断栈是否为空

Stack.c

bool STEmpty(st* pst)
{assert(pst);return pst->top == 0;
}

3.8 顺序栈打印栈内元素

Stack.c

void STPrint(st* pst)
{assert(pst);while(!STEmpty(pst)){printf("%d\n", STTop(pst));STPop(pst);}
}

3.9 顺序栈销毁栈

Stack.c

void STDestroy(st* pst)
{assert(pst);free(pst->arr);pst->arr = NULL;pst->top = pst->capacity = 0;
}

3.10 完整代码

Stack.h

//需要用到的库函数的头文件
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int STDataType;typedef struct Stack
{STDataType* arr;//指向动态开辟的一块空间int top;//指向栈顶元素的下一个位置int capacity;//栈的容量
}st;//初始化栈
void STInit(st* pst);
//入栈
void STPush(st* pst,STDataType x);
//出栈
void STPop(st* pst);
//获取栈顶元素
STDataType STTop(st* pst);
//获取栈内有效数据个数
int STSize(st* pst);
//判断栈是否为空
bool STEmpty(st* pst);
//打印栈内元素
void STPrint(st* pst);
//销毁栈
void STDestroy(st* pst);

Stack.c

#include"Stack.h"void STInit(st* pst)
{//判断pst是否为空指针assert(pst);pst->arr = NULL;pst->capacity = pst->top = 0;
}void STPush(st* pst, STDataType x)
{//判断pst是否为空指针assert(pst);//检查空间大小,是否需要增容if (pst->capacity == pst->top){int newcapacity = pst->capacity == 0 ? 4 :2 * pst->capacity;STDataType* tmp = (STDataType*)realloc(pst->arr,newcapacity * sizeof(STDataType));if (tmp == NULL){perror("realloc");exit(1);}pst->arr = tmp;pst->capacity = newcapacity;}//入数据pst->arr[pst->top++] = x;
}void STPop(st* pst)
{//判断pst是否为空指针assert(pst);//判断栈是否为空assert(pst->top > 0);pst->top--;
}STDataType STTop(st* pst)
{//判断pst是否为空指针assert(pst);//判断栈是否为空assert(pst->top > 0);//这里top指向栈顶元素的下一个位置,-1刚好指向栈顶元素return pst->arr[pst->top - 1];
}int STSize(st* pst)
{assert(pst);return pst->top;
}bool STEmpty(st* pst)
{assert(pst);return pst->top == 0;
}void STPrint(st* pst)
{assert(pst);while(!STEmpty(pst)){printf("%d\n", STTop(pst));STPop(pst);}
}void STDestroy(st* pst)
{assert(pst);free(pst->arr);pst->arr = NULL;pst->top = pst->capacity = 0;
}

4. 链式栈的实现

Stack.h

//需要用到的库函数的头文件
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int STDataType;typedef struct StackNode //定义节点结构
{STDataType data;struct StackNode* next;
}stnode;typedef struct Stack //定义栈结构
{//记录栈顶stnode* top;//记录栈内有效数据个数int size;
}st;//初始化栈
void STInit(st* pst);
//入栈
void STPush(st* pst, STDataType x);
//出栈
void STPop(st* pst);
//获取栈顶元素
STDataType STTop(st* pst);
//获取栈内有效数据个数
int STSize(st* pst);
//判断栈是否为空
bool STEmpty(st* pst);
//打印栈内元素
void STPrint(st* pst);
//销毁栈
void STDestroy(st* pst);

4.1 链式栈的声明

Stack.h

typedef int STDataType;typedef struct StackNode //定义节点结构
{STDataType data;struct StackNode* next;
}stnode;typedef struct Stack //定义栈结构
{//记录栈顶stnode* top;//记录栈内有效数据个数int size;
}st;

这里如果我们用单链表的形式实现栈的话,单链表头插的时间复杂度为O(1),尾插的时间复杂度为O(N),所以这里我们需要把单链表的第一个有效节点设置为栈顶,如果想把链表的尾设置尾栈顶的话,那我们这里可能就需要使用双向链表来实现了,本章主要使用单链表来实现栈。

我们在声明链式栈的时候,我们这里使用了两个结构体,其中一个结构体声明链式栈里的数据节点,另一个结构体声明栈的结构,其中里面有个指向栈顶指针,还有一个记录栈内有效数据个数的size,这样我们在获取栈内有效数据个数的时候不需要遍历链表,时间复杂度从O(N)变成O(1)

4.2 链式栈的初始化

Stack.c

void STInit(st* pst)
{assert(pst);pst->top = NULL;pst->size = 0;
}

4.3 链式栈的入栈

Stack.c

void STPush(st* pst, STDataType x)
{assert(pst);stnode* newnode = (stnode*)malloc(sizeof(stnode));if (newnode == NULL){perror("malloc");exit(1);}newnode->data = x;newnode->next = NULL;if (pst->top== NULL){pst->top = newnode;}else{newnode->next = pst->top;pst->top = newnode;}pst->size++;
}

4.4 链式栈的出栈

Stack.c

void STPop(st* pst)
{assert(pst);assert(pst->size > 0);stnode* next = pst->top->next;free(pst->top);pst->top = next;pst->size--;
}

4.5 链式栈获取栈顶元素

Stack.c

STDataType STTop(st* pst)
{assert(pst);assert(pst->size > 0);return pst->top->data;
}

4.6 链式栈获取栈内有效数据个数

Stack.c

int STSize(st* pst)
{assert(pst);return pst->size;
}

4.7 链式栈判断栈是否为空

Stack.c

bool STEmpty(st* pst)
{assert(pst);return pst->size == 0;
}

4.8 链式栈打印栈内元素

Stack.c

void STPrint(st* pst)
{assert(pst);while (!STEmpty(pst)){printf("%d\n",STTop(pst));STPop(pst);}
}

4.9 链式栈销毁栈

Stack.c

void STDestroy(st* pst)
{assert(pst);stnode* pcur = pst->top;while (pcur){stnode* next = pcur->next;free(pcur);pcur = next;}pst->top = NULL;pst->size = 0;
}

4.10 完整代码

Stack.h

//需要用到的库函数的头文件
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int STDataType;typedef struct StackNode //定义节点结构
{STDataType data;struct StackNode* next;
}stnode;typedef struct Stack //定义栈结构
{//记录栈顶stnode* top;//记录栈内有效数据个数int size;
}st;//初始化栈
void STInit(st* pst);
//入栈
void STPush(st* pst, STDataType x);
//出栈
void STPop(st* pst);
//获取栈顶元素
STDataType STTop(st* pst);
//获取栈内有效数据个数
int STSize(st* pst);
//判断栈是否为空
bool STEmpty(st* pst);
//打印栈内元素
void STPrint(st* pst);
//销毁栈
void STDestroy(st* pst);

Stack.c

void STInit(st* pst)
{assert(pst);pst->top = NULL;pst->size = 0;
}void STPush(st* pst, STDataType x)
{assert(pst);stnode* newnode = (stnode*)malloc(sizeof(stnode));if (newnode == NULL){perror("malloc");exit(1);}newnode->data = x;newnode->next = NULL;if (pst->top== NULL){pst->top = newnode;}else{newnode->next = pst->top;pst->top = newnode;}pst->size++;
}void STPop(st* pst)
{assert(pst);assert(pst->size > 0);stnode* next = pst->top->next;free(pst->top);pst->top = next;pst->size--;
}STDataType STTop(st* pst)
{assert(pst);assert(pst->size > 0);return pst->top->data;
}int STSize(st* pst)
{assert(pst);return pst->size;
}bool STEmpty(st* pst)
{assert(pst);return pst->size == 0;
}void STPrint(st* pst)
{assert(pst);while (!STEmpty(pst)){printf("%d\n",STTop(pst));STPop(pst);}
}void STDestroy(st* pst)
{assert(pst);stnode* pcur = pst->top;while (pcur){stnode* next = pcur->next;free(pcur);pcur = next;}pst->top = NULL;pst->size = 0;
}

5. 疑问

我们发现,我们很多的接口函数只有几行代码,有必要单独写成一个函数吗?比如说出栈,我们直接通过访问结构体进行删除。

其实有必要的,为了规范一定的写法,保持一致性,我们可以通过接口来调用它的函数,不建议自行访问,因为我们不知道设计者在设计top的时候,top是指向栈顶元素还是栈顶元素的下一个位置。

所以,不管再简单,数据结构里面不要直接访问数据,建议调用它的接口方法。

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

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

相关文章

[数据集][目标检测]鸡蛋缺陷检测数据集VOC+YOLO格式2918张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2918 标注数量(xml文件个数)&#xff1a;2918 标注数量(txt文件个数)&#xff1a;2918 标注…

什么是大语言模型

前言 自从去年chatgpt横空出世以来&#xff0c;它火爆也让大语言模型这个词变的很流行&#xff0c;到底什么是大语言模型&#xff0c;今天从初学者的角度介绍一下大语言模型的基本概念、组成部分和基本工作流程等。下面的介绍中如果涉及到一些专业术语不太理解&#xff0c;也没…

携程暑期实习一面

携程暑期实习一面 4.7 50min 微核 两段实习时间节点&#xff0c;为什么想这么早去实习。讲一下测试工作的流程是什么样子的对于测试用例方法设计上你了解哪些讲一下你所提到的等价类划分法和边界值分析法的具体概念&#xff0c;还了解哪些方法一个场景&#xff0c;web端或者…

算法金 | 决策树、随机森林、bagging、boosting、Adaboost、GBDT、XGBoost 算法大全

大侠幸会&#xff0c;在下全网同名「算法金」 0 基础转 AI 上岸&#xff0c;多个算法赛 Top 「日更万日&#xff0c;让更多人享受智能乐趣」 决策树是一种简单直观的机器学习算法&#xff0c;它广泛应用于分类和回归问题中。它的核心思想是将复杂的决策过程分解成一系列简单的决…

AI学习指南机器学习篇-朴素贝叶斯的优缺点

AI学习指南机器学习篇-朴素贝叶斯的优缺点 在机器学习领域&#xff0c;朴素贝叶斯算法是一种常见且有效的分类方法。它基于贝叶斯定理和特征条件独立性假设&#xff0c;广泛应用于文本分类、垃圾邮件过滤、情感分析等领域。然而&#xff0c;朴素贝叶斯算法也存在一些局限性和缺…

【推荐】Prometheus+Grafana企业级监控预警实战

新鲜出炉&#xff01;&#xff01;&#xff01;PrometheusGrafanaAlertmanager springboot 企业级监控预警实战课程&#xff0c;从0到1快速搭建企业监控预警平台&#xff0c;实现接口调用量统计&#xff0c;接口请求耗时统计…… 详情请戳 https://edu.csdn.net/course/detai…

Word页码设置,封面无页码,目录摘要阿拉伯数字I,II,III页码,正文开始123为页码

一、背景 使用Word写项目书或论文时&#xff0c;需要正确插入页码&#xff0c;比如封面无页码&#xff0c;目录摘要阿拉伯数字I&#xff0c;II&#xff0c;III为页码&#xff0c;正文开始以123为页码&#xff0c;下面介绍具体实施方法。 所用Word版本&#xff1a;2021 二、W…

HTTPS 代理的优点和缺点是什么?

HTTPS&#xff08;超文本安全传输协议&#xff09;作为一种基于HTTP加上SSL安全层的网络通信协议&#xff0c;已经成为互联网上广泛使用的IP协议之一。它在保证信息安全和隐私方面具有很多优势&#xff0c;但也存在一些缺点。接下来&#xff0c;我们就来探究一下HTTPS协议的优缺…

探索加拿大IT行业的移民优势与职业前景

随着全球化的不断深入&#xff0c;人才流动已成为推动经济发展的重要力量。加拿大以其开放的移民政策和对高技能人才的渴求&#xff0c;成为全球IT专业人士向往的目的地。 一、快速移民通道 加拿大政府高度重视IT行业人才&#xff0c;为IT专业人士提供了快速移民通道。根据最…

Qt篇——获取Windows系统上插入的串口设备的物理序号

先右键【此电脑-管理- 设备管理器-端口&#xff08;COM和LPT&#xff09;】中找到我们插入的某个设备的物理序号&#xff0c;如下图红色矩形框出的信息&#xff0c;这个就是已插入设备的物理序号&#xff08;就是插在哪个USB口的意思&#xff09;。 在Linux下我们可以通过往/et…

零撸创业项目:撸广告小游戏app开发源码

看广告小游戏APP的开发涉及到一系列复杂的步骤和考量&#xff0c;以下是主要的开发流程和需要注意的事项&#xff1a; 开发流程 市场调研&#xff1a; 深入了解当前小游戏市场和广告市场的趋势&#xff0c;以及用户的需求和喜好1。 分析竞争对手的产品&#xff0c;找出自己的…

【Android JNI】 C/C++ 标准输入输出打印至Android日志控制台

【Android】 C/C 标准输入输出打印至Android日志控制台 #if defined(__ANDROID__) start_logger("yeasound_sdk-native"); #endif#if defined(__ANDROID__) #include <stdio.h> #include <unistd.h> #include <android/log.h> #include <androi…

2024运维人该何去何从?

文章目录 概要写作背景当下运维行业现状未来的个人提升小结 概要 互联网自诞生之日起到今天&#xff0c;一直保持着高速发展的状态&#xff0c;每一次互联网的革新&#xff0c;都会带来一大批的机遇&#xff0c;而现在我会在这里讲的是2024年&#xff0c;作为一个it运维&#…

【踩坑】修复循环设置os.environ[‘CUDA_VISIBLE_DEVICES‘]无效

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 问题示例 for gpus in [0, 1, 2, 3, 4, 5, 6, 7]:os.environ[CUDA_VISIBLE_DEVICES] gpusprint(torch.cuda.get_device_name(0)) 始终将使用第…

Mac安装多版本node

Mac下使用n模块去安装多个指定版本的Node.js&#xff0c;并使用命令随时切换。 node中的n模块是&#xff0c;node专门用来管理node版本的模块&#xff0c;可以进行node版本的切换&#xff0c;下载&#xff0c;安装。 1.安装n npm install -g n 2.查看版本 n --version 3.展…

C# —— 子类访问父类

创建子类对像时候 默认调用父类的无参数的构造函数&#xff0c;不会调用带参数构造函数 创建子类对象的时候可以调用父类的带参数的构造函数&#xff0c;在子类的构造函数后面:base()调用父类的构造 动物类 父类 class Animal {public string Name { get; set; }public cha…

游戏心理学Day25

游戏心理学的基本研究方法 科学解释的本质 实证观察和自我纠正是科学方法的两个特征无论是&#xff0c;观察还是系统的观察都可能导致理论的产生理论又反过来影响市政形成一种循环和自我纠正科学中使用的最基本的要素是数据和理论。 现代科学家强调数据的重要性&#xff0c;把…

动作捕捉与数字人实训室,引领动漫专业创新发展

如今&#xff0c;随着全身动作捕捉设备在动漫行业中的应用越来越重要&#xff0c;传统的教学模式与市场需求逐渐脱节&#xff0c;原有的教学方式和思路急需进行调整。高校通过搭建动作捕捉与数字人实训室&#xff0c;可以使得教学质量和效率大大提升&#xff0c;让学生能够接触…

如何采集拼多多的商品或店铺数据

怎么使用简数采集器批量采集拼多多的商品或店铺相关信息呢&#xff1f; 简数采集器暂时不支持采集拼多多的商品或店铺相关数据&#xff0c;只能采集页面公开显示的信息&#xff0c;谢谢。 简数采集器采集网站文章资讯等数据特别简单高效&#xff1a;只需输入网站网址&#xf…

由浅入深,走进深度学习(补充篇:神经网络基础)

在编程实战中&#xff0c;基础是最重要的&#xff0c;所以为了巩固基础&#xff0c;哈哈哈~ 不说废话了&#xff0c;大家喜欢就往下看看&#xff0c;也是我自己的一些总结&#xff0c;方便以后自己看~ 我觉得还是动手敲一遍&#xff0c;会有不一样的感受~ 相关内容&#xff…