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

C语言_数据结构总结6:链式栈

 纯c语言代码,不涉及C++

顺序栈的实现,欢迎查看这篇文章:C语言_数据结构总结5:顺序栈-CSDN博客 

0. 结构单元

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

typedef int ElemType;
typedef struct Linknode {
    ElemType data;  //数据域
    struct Linknode* next;  //指针域
}LinkStack;

1.1  初始化(返回头结点) 推荐

LinkStack* InitLinkStack1() {LinkStack* L = (LinkStack*)malloc(sizeof(LinkStack));  //定义一个头结点(栈顶结点)if (L == NULL) {printf("内存分配失败\n");return NULL;}L->next = NULL;return L;
}

1.2  初始化方法2(不返回头结点指针) 不推荐

void InitLinkStack2(LinkStack** L) {*L = (LinkStack*)malloc(sizeof(LinkStack));  //定义一个头结点if (*L == NULL){printf("内存分配失败!\n");return;  //提前结束该函数}(*L)->next = NULL;
}

首先:在链式栈的初始化操作中,返回头结点的指针是一种常见且推荐的做法,但并不是绝对必须的,
推荐返回头结点指针的原因:
1.方便后续操作
返回头结点的指针可以让调用者方便地对链式栈进行后续操作。因为链式栈的各种操作(如入栈、出栈、获取栈顶元素等)都需要通过头结点来访问栈中的元素,调用者持有头结点的指针后,就可以直接将其作为参数传递给这些操作函数

2.符合模块化设计原则
将初始化操作设计为返回头结点指针,使得初始化函数的功能更加独立和清晰。调用者可以明确知道初始化函数会返回一个指向链式栈头结点的指针,便于在不同的代码模块中复用该函数。

2. 1 判空方法1 (采用指针传递) 推荐

int LinkStackEmpty1(LinkStack* L) {return L->next == NULL;
}

2.2  判空方法2 (采用值传递) 不推荐

int LinkStackEmpty2(LinkStack L) {return L.next == NULL;
}

首先:从语法和功能角度来看,传入 LinkStack L 是可行的。
因为判空操作仅仅是读取栈的信息,不涉及对栈结构的修改,所以即使采用值传递,也能正确完成判空的逻辑。

在上述代码中,LinkStackEmpty2 函数接收的是 LinkStack 类型的变量,通过判断其 next 指针是否为 NULL 来确定栈是否为空。

不建议值传递(传入LinkStack L)的原因(其它类似操作以此类推):
1. 内存开销较大
当使用值传递(传入 LinkStack L)时,会在函数调用时复制整个 LinkStack 结构体。
对于链式栈来说,虽然结构体本身可能不大,但复制操作仍然会带来额外的内存开销。而使用指针传递(传入 LinkStack* L),只需要复制一个指针,其内存开销远远小于复制整个结构体。

2. 性能问题
值传递的复制操作会消耗一定的时间,尤其是在结构体较大或者频繁调用判空函数的情况下,会对程序的性能产生影响。指针传递则避免了这种复制操作,能够提高程序的执行效率。

3. 代码一致性
在链式栈的其他操作(如入栈、出栈等)中,通常需要修改栈的结构,因此需要使用指针传递。为了保持代码的一致性,建议在判空操作中也使用指针传递,这样可以让代码更加统一和易于理解。

3. 入栈

int push(LinkStack* L, ElemType value) {LinkStack* s = (LinkStack*)malloc(sizeof(LinkStack));if (s == NULL){printf("内存分配失败!\n");return -2;}s->data = value;s->next = L->next;L->next = s;return 0;  //入栈(插入成功)
}

4. 出栈

 value:用于存储出栈元素的值,是一个指向元素类型的指针

int pop(LinkStack* L, ElemType* value) {if (LinkStackEmpty1(L)){printf("栈空,无法出栈!\n");return -2;}LinkStack* p = L->next;*value = p->data;L->next = p->next;free(p);return 0;  //出栈成功}

5. 获取栈顶元素

int gettopValue(LinkStack* L, ElemType* value) {if (LinkStackEmpty1(L)){printf("栈空,无法获取元素!\n");return -2;}LinkStack* p = L->next;*value = p->data;  // 合并:*value = L->next->datareturn 0;  //获取栈顶信息成功
}

6. 打印栈内元素

void printLinkStack(LinkStack L) {if (LinkStackEmpty1(&L)){printf("栈空,无法获取元素!\n");return;}LinkStack* p = L.next;printf("栈中的元素(从栈顶到栈底)为: ");while (p != NULL) {printf("%d ", p->data);p = p->next;}printf("\n-----------------------------------------------\n");
}

7.销毁

void destroyLinkStack(LinkStack* L) {LinkStack* p = L;while (p != NULL) {LinkStack* s = p;p = p->next;free(s);}
}

8.测试

int main() {//第二种初始化方式LinkStack* L1;  // 定义一个头指针InitLinkStack2(&L1);  // 初始化时,将该头指针指向了头结点//第一种初始化方式LinkStack* L = InitLinkStack1();if (L == NULL){return -1;  }printLinkStack(*L);  // 栈空,无法获取元素!// 测试进栈操作push(L, 11);push(L, 22);push(L, 33);printLinkStack(*L);  // 栈中的元素(从栈顶到栈底)为: 33 22 11// 获取栈顶元素ElemType value;if (gettopValue(L,&value) == 0){printf("当前栈顶元素为:%d\n", value);  // 当前栈顶元素为:33}// 测试出栈操作if (pop(L, &value) == 0) {printf("出栈的元素为:%d\n", value);  // 出栈的元素为:33}printf("元素出栈后,");printLinkStack(*L);  // 元素出栈后,栈中的元素(从栈顶到栈底)为: 22 11// 销毁栈destroyLinkStack(L);return 0;
}

9. 完整代码

//链式栈的基本实现
//头结点的下一个结点是栈顶元素
#include<stdio.h>
#include<stdlib.h>typedef int ElemType;
typedef struct Linknode {ElemType data;  //数据域struct Linknode* next;  //指针域
}LinkStack;// 操作1——初始化(返回头结点) 推荐
LinkStack* InitLinkStack1() {LinkStack* L = (LinkStack*)malloc(sizeof(LinkStack));  //定义一个头结点(栈顶结点)if (L == NULL) {printf("内存分配失败\n");return NULL;}L->next = NULL;return L;
}// 操作1.1——初始化(不返回头结点指针) 不推荐
void InitLinkStack2(LinkStack** L) {*L = (LinkStack*)malloc(sizeof(LinkStack));  //定义一个头结点if (*L == NULL){printf("内存分配失败!\n");return;  //提前结束该函数}(*L)->next = NULL;
}// 操作2——判空(采用指针传递) 推荐
int LinkStackEmpty1(LinkStack* L) {return L->next == NULL;
}// 操作2.1——判空(采用值传递) 不推荐
int LinkStackEmpty2(LinkStack L) {return L.next == NULL;
}// 操作3——入栈
int push(LinkStack* L, ElemType value) {LinkStack* s = (LinkStack*)malloc(sizeof(LinkStack));if (s == NULL){printf("内存分配失败!\n");return -2;}s->data = value;s->next = L->next;L->next = s;return 0;  //入栈(插入成功)
}// 操作4——出栈,类似于链表的头删法
// value:用于存储出栈元素的值,是一个指向元素类型的指针
int pop(LinkStack* L, ElemType* value) {if (LinkStackEmpty1(L)){printf("栈空,无法出栈!\n");return -2;}LinkStack* p = L->next;*value = p->data;L->next = p->next;free(p);return 0;  //出栈成功}// 操作5——获取栈顶元素
int gettopValue(LinkStack* L, ElemType* value) {if (LinkStackEmpty1(L)){printf("栈空,无法获取元素!\n");return -2;}LinkStack* p = L->next;*value = p->data;  // 合并:*value = L->next->datareturn 0;  //获取栈顶信息成功
}// 操作6——打印栈
void printLinkStack(LinkStack L) {if (LinkStackEmpty1(&L)){printf("栈空,无法获取元素!\n");return;}LinkStack* p = L.next;printf("栈中的元素(从栈顶到栈底)为: ");while (p != NULL) {printf("%d ", p->data);p = p->next;}printf("\n-----------------------------------------------\n");
}// 操作7——销毁栈
void destroyLinkStack(LinkStack* L) {LinkStack* p = L;while (p != NULL) {LinkStack* s = p;p = p->next;free(s);}
}int main() {//第二种初始化方式LinkStack* L1;  // 定义一个头指针InitLinkStack2(&L1);  // 初始化时,将该头指针指向了头结点//第一种初始化方式LinkStack* L = InitLinkStack1();if (L == NULL){return -1;  }printLinkStack(*L);  // 栈空,无法获取元素!// 测试进栈操作push(L, 11);push(L, 22);push(L, 33);printLinkStack(*L);  // 栈中的元素(从栈顶到栈底)为: 33 22 11// 获取栈顶元素ElemType value;if (gettopValue(L,&value) == 0){printf("当前栈顶元素为:%d\n", value);  // 当前栈顶元素为:33}// 测试出栈操作if (pop(L, &value) == 0) {printf("出栈的元素为:%d\n", value);  // 出栈的元素为:33}printf("元素出栈后,");printLinkStack(*L);  // 元素出栈后,栈中的元素(从栈顶到栈底)为: 22 11// 销毁栈destroyLinkStack(L);return 0;
}

10. 运行截图

本人菜鸟一只,文章如有问题,欢迎评论区指正!

相关文章:

C语言_数据结构总结6:链式栈

纯c语言代码&#xff0c;不涉及C 顺序栈的实现&#xff0c;欢迎查看这篇文章&#xff1a;C语言_数据结构总结5&#xff1a;顺序栈-CSDN博客 0. 结构单元 #include<stdio.h> #include<stdlib.h> typedef int ElemType; typedef struct Linknode { ElemType…...

DQN(Deep Q - Network)原理举例说明

DQN(Deep Q - Network)原理举例说明 1. 基本概念回顾 DQN 结合了深度学习和 Q - learning 算法,用深度神经网络来近似 Q 值函数,解决传统 Q - learning 在处理高维状态空间时的局限性。Q 值表示在某个状态下采取某个动作所能获得的期望累积奖励。 以下是DQN和A3C的原理对…...

物联网-IoTivity:开源的物联网框架

IoTivity 是一个开源的物联网(IoT)框架,旨在为物联网设备提供互操作性、安全性和可扩展性。它由 Open Connectivity Foundation (OCF) 主导开发,遵循 OCF 的标准,致力于实现设备之间的无缝连接和通信。IoTivity 提供了一个统一的框架,支持设备发现、数据交换、设备管理和…...

基于DeepSeek的智慧医药系统(源码+部署教程)

运行环境 智慧医药系统运行环境如下&#xff1a; 前端&#xff1a; HTMLCSS后端&#xff1a;Java AIGCDeepseekIDE工具&#xff1a;IDEA技术栈&#xff1a;Springboot HTMLCSS MySQL 主要角色 智慧医药系统主要分为两个角色。 游客 尚未进行注册和登录。具备登录注册、…...

基于Linux系统的边缘智能终端(RK3568+EtherCAT+PCIe+4G+5G)

背景 现有产品基本都是传统的产品&#xff0c;比如之前写的RTU还有基于Linux系统的物联网采集终端都是传统意义的产品&#xff0c;大家做的都差不多&#xff0c;能拼的除了价格之外就是软硬件的基本功了&#xff0c;好的产品肯定是要经过时间的磨合的。没有任何人可以写出来没有…...

Java 线程池内部任务出异常后,如何知道是哪个线程出了异常?

你的回答&#xff08;口语化&#xff0c;面试场景&#xff09; 好的&#xff0c;这个问题需要结合线程池的异常处理机制来回答。 Java线程池内部任务抛出的异常默认会被“吞掉”&#xff0c;但可以通过以下方法定位具体线程的异常&#xff1a; 方法1&#xff1a;在任务代码中捕…...

热图回归(Heatmap Regression)

热图回归(Heatmap Regression)是一种常用于关键点估计任务的方法,特别是在人体姿态估计中。它的基本思想是通过生成热图来表示某个关键点在图像中出现的概率或强度。以下是热图回归的主要特点和工作原理: 主要特点 热图表示: 每个关键点对应一个热图,热图中的每个像素值…...

信奥赛CSP-J复赛集训(模拟算法专题)(6):P6352 [COCI 2007/2008 #3] CETIRI

信奥赛CSP-J复赛集训&#xff08;模拟算法专题&#xff09;&#xff08;6&#xff09;&#xff1a;P6352 [COCI 2007/2008 #3] CETIRI 题目描述 你原本有 4 4 4 个数&#xff0c;它们从小到大排序后构成了等差数列。 但是现在丢失了一个数&#xff0c;并且其余的三个数的顺序…...

2025-03-09 学习记录--C/C++-PTA 习题11-1 输出月份英文名

合抱之木&#xff0c;生于毫末&#xff1b;九层之台&#xff0c;起于累土&#xff1b;千里之行&#xff0c;始于足下。&#x1f4aa;&#x1f3fb; 一、题目描述 ⭐️ 裁判测试程序样例&#xff1a; #include <stdio.h>char *getmonth( int n );int main() {int n;char …...

spring IOC(实现原理)

文章目录 依赖注入控制反转相关Spring 框架的 Bean管理的配置文件方式实例化Bean的三种方式无参构造器实例化静态工厂方法实例化实例工厂方法实例化静态和动态对比 注解常用注解纯注解 其它问题为什么p 命名空间方式需要无参构造 依赖注入 **依赖注入&#xff08;DI&#xff0…...

linux环保监测4G边缘网关:环境数据的可靠传输者

环保监测工控机&#xff0c;常被称为“环境数据采集器”或“环保数据终端”&#xff0c;是一种专门用于环境监测领域的工业计算机。它具备强大的数据处理能力、稳定的运行性能和多种接口&#xff0c;能够实时采集、处理和传输环境监测数据。这些数据包括空气质量、水质、噪声、…...

【哇! C++】类和对象(五) - 赋值运算符重载

目录 ​编辑 一、运算符重载 1.1 运算符重载概念 1.2 全局运算符重载 1.3 运算符重载为成员函数 二、赋值运算符重载的特性 2.1 赋值运算符重载需要注意的点 2.2 赋值运算符重载格式 2.2.1 传值返回 2.2.2 传引用返回 2.2.3 检查自己给自己赋值 三、赋值运算符重载的…...

基于单片机的风速报警装置设计

标题:基于单片机的风速报警装置设计 内容:1.摘要 本设计聚焦于基于单片机的风速报警装置&#xff0c;旨在解决传统风速监测缺乏实时报警功能的问题。采用单片机作为核心控制单元&#xff0c;结合风速传感器采集风速数据。经实验测试&#xff0c;该装置能准确测量 0 - 60m/s 范…...

Linux一键安装zsh终端美化插件

zsh应该是很多人第一个用的Linux终端美化软件 但是其安装略微复杂&#xff0c;让人有些困扰 所以我花了两天写了一键安装脚本&#xff0c;实测运行后直接安装好 适用于Ubuntu、Debian、Red Hat、macOS等系统 直接安装好zsh 以及常用插件 autojump 跳转插件 zsh-syntax-highlig…...

Docker部署Ragflow(完美解决502 bad gateway)

Docker快速启动Ragflow:Dev 系统准备 ubuntu server 24.04 CPU ≥ 4 cores (x86);RAM ≥ 16 GB;Disk ≥ 100 GB; 更新系统 sudo apt update 下载源码 git clone https://github.com/infiniflow/ragflow.git cd ragflow/docker # 切换稳定版本分支 git checkout -f v0.17.…...

C++ 算法竞赛STL以及常见模板

目录 STL /*═══════════════ Vector ═══════════════*/ /*════════════════ Pair ════════════════*/ /*══════════════ String ════════════════*/ /*══════════…...

前端数据模拟 Mock.js 学习笔记(附带详细)

前端数据模拟 Mock.js 学习笔记 在前端开发过程中&#xff0c;数据模拟是一项至关重要的环节。当后端接口尚未完成或者需要独立进行前端开发与测试时&#xff0c;Mock.js 能发挥巨大作用&#xff0c;它可以模拟各种数据场景&#xff0c;助力前端开发高效进行。 一、Mock.js 的…...

Web基础:HTML快速入门

HTML基础语法 HTML&#xff08;超文本标记语言&#xff09; 是用于创建网页内容的 标记语言&#xff0c;通过定义页面的 结构和内容 来告诉浏览器如何呈现网页。 超文本&#xff08;Hypertext&#xff09; 是一种通过 链接&#xff08;Hyperlinks&#xff09; 将不同文本、图像…...

学习一下Qt中的分裂器以及为什么要使用分裂器?

关于分裂器的使用和内容这篇文章写的很详细了[QT_043]Qt学习之分裂器&#xff08;QSplitter&#xff09;-CSDN博客 我就说一下为什么已经有布局器要还使用分裂器 在 Qt 中&#xff0c;布局时使用分裂器&#xff08;QSplitter&#xff09;主要有以下几个方面的好处&#xff…...

Xshell链接Linux机器更换jdk版本

都25年了&#xff0c;不会还有人在用 jdk1.8 吧? 那么问题来了&#xff0c;如果公司让你更新机器上的jdk版本&#xff0c;你又刚好是一个小白没操作过&#xff0c;怎么办&#xff1f;别急&#xff0c;步骤这就奉上~ 编辑 .bashrc 或 .zshrc 配置文件&#xff1a; 如果你使用的…...

2025.3.3总结

周一这天&#xff0c;我约了绩效教练&#xff0c;主要想了解专业类绩效的考核方式以及想知道如何拿到一个更好的绩效。其他的岗位并不是很清楚&#xff0c;但是专业类的岗位&#xff0c;目前采取绝对考核&#xff0c;管理层和专家岗采取相对考核&#xff0c;有末尾淘汰。 通过…...

如何应用大模型 — 大模型使用范式

从OpenAI发布ChatGPT开始&#xff0c;大模型就开始受到大家关注&#xff0c;到DeepSeek-R1出现&#xff0c;大家的关注达到了顶峰&#xff0c;越来越多的企业&#xff0c;机构&#xff0c;学校&#xff0c;政府部门希望接入大模型&#xff0c;希望通过大模型来提升效率&#xf…...

DeepSeek本机部署(基于Ollama和Docker管理)

目录 一、ollama 与 docker 简介 &#xff08;一&#xff09;ollama(Ollama) &#xff08;二&#xff09;docker 二、利用 ollama 和 docker 配置 deepseek-r1 的准备工作 &#xff08;一&#xff09;硬件需求 &#xff08;二&#xff09;软件安装 三、配置 deepseek-r1…...

C++复试笔记(一)

Setw 是C中用于设置输出字段宽度的函数。当使用 setw(3) 时&#xff0c;它会设置紧接着的输出字段的最小宽度为3个字符。如果字段内容长度小于3&#xff0c;则会在左侧填充空格以达到指定宽度&#xff1b;如果内容长度大于或等于3&#xff0c;则全部内容将被输出&#xff0c;…...

如何让一个类作为可调用对象被thread调用?

如何让一个类作为可调用对象&#xff0c;被 std::thread 调用 在 C 中&#xff0c;可以让一个类对象作为可调用对象&#xff08;Callable Object&#xff09;&#xff0c;然后用 std::thread 进行调用。要实现这一点&#xff0c;主要有三种方法&#xff1a; 重载 operator()&…...

学习小程序开发--Day1

项目学习开篇 项目架构 项目进程 创建uni-app项目 通过HBuilderX创建 小结 page.json 和 tabBar 目录文件 pages.json的配置...

“量子心灵AI“的监控仪表盘 - javascript网页设计案例

【前端实战】基于Three.js和Chart.js打造未来科技风AI监控仪表盘 本文通过AI辅助开发&#xff0c;详细记录了一个高级前端项目的完整实现过程。文章包含核心代码片段、技术要点及遇到的问题与解决方案。适合有一定前端基础的开发者学习参考。 1. 项目概述 本文详细介绍了一个名…...

Redis 中 string 和 list 的原理说明

Redis 中 string 和 list 的底层实现 Redis有5种基础数据结构&#xff0c;对应的value分别为&#xff1a;string (字符串)、list (列表)、set (集合)、hash (哈希) 和 zset (有序集合) Redis 对象头结构体&#xff1a; struct RedisObject {int4 type; // 4bits 对象的基本类型…...

JVM常用概念之String.intern()

问题 String.intern()的工作原理&#xff1f;我们应该如何使用它? 基础知识 字符串池&#xff08;String Pool&#xff09; String类在我们日常编程工作中是使用频率非常高的一种对象类型。JVM为了提升性能和减少内存开销&#xff0c;避免字符串的重复创建&#xff0c;其维…...

DeepLabv3+改进6:在主干网络中添加SegNext_Attention|助力涨点

🔥【DeepLabv3+改进专栏!探索语义分割新高度】 🌟 你是否在为图像分割的精度与效率发愁? 📢 本专栏重磅推出: ✅ 独家改进策略:融合注意力机制、轻量化设计与多尺度优化 ✅ 即插即用模块:ASPP+升级、解码器 PS:订阅专栏提供完整代码 目录 论文简介 步骤一 步骤二…...