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. 运行截图

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

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

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

相关文章

新品速递 | 多通道可编程衰减器+矩阵系统,如何破解复杂通信测试难题?

在无线通信技术快速迭代的今天&#xff0c;多通道可编程数字射频衰减器和衰减矩阵已成为测试领域不可或缺的核心工具。它们凭借高精度、灵活配置和强大的多通道协同能力&#xff0c;为5G、物联网、卫星通信等前沿技术的研发与验证提供了关键支持。从基站性能测试到终端设备校准…

AI自动化应用的影响

生产力的迭代也终将伴随着一代人的落幕。 2025年是AI应用爆发的开局之年&#xff0c;预计3-5年现有生产关系将出现颠覆性改革。 AI自动化对经济和就业的影响是一个复杂且多维的问题&#xff0c;其长期影响取决于技术进步、政策调控、社会适应能力等多重因素的综合作用。以下从技…

润开鸿重磅首发基于“RISC-V+OpenHarmony+星闪”的“鸿锐”AI开发平台

润开鸿重磅首发基于“RISC-VOpenHarmony星闪”的“鸿锐”AI开发平台 2月28日&#xff0c;2025中国RISC-V生态大会在北京中关村国际创新中心隆重召开。作为领先的鸿蒙生态专业技术公司和终端操作系统发行版提供商&#xff0c;以及不断推进基于RISC-V与OpenHarmony全栈开源生态构…

Java 深度复制对象:从基础到实战

目录 一、深度复制的概念二、实现深度复制的方法1. 使用序列化2. 手动实现深度复制 三、总结 在 Java 编程中&#xff0c;对象的复制是一个常见的需求。然而&#xff0c;简单的复制操作&#xff08;如直接赋值&#xff09;只会复制对象的引用&#xff0c;而不是创建一个新的对象…

C++ Primer 交换操作

欢迎阅读我的 【CPrimer】专栏 专栏简介&#xff1a;本专栏主要面向C初学者&#xff0c;解释C的一些基本概念和基础语言特性&#xff0c;涉及C标准库的用法&#xff0c;面向对象特性&#xff0c;泛型特性高级用法。通过使用标准库中定义的抽象设施&#xff0c;使你更加适应高级…

FFmpeg-chapter7和chapter8-使用 FFmpeg 解码视频(原理篇和实站篇)

解码流程如下图 流程&#xff1a;首先&#xff0c;通过 avcodec_alloc_context3(nullptr) 分配一个 AVCodecContext 结构体&#xff0c;然后使用 avcodec_parameters_to_context 将参数复制到上下文中&#xff0c;接着通过 avcodec_find_decoder 查找指定的解码器&#xff0c;并…

2025 docker安装TiDB数据库

1.确保安装了docker和docker-compose sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-composesudo chmod x /usr/local/bin/docker-compose2.编写 Docker Compose 文…

定制化开发的WooCommerce独立站商城更安全

定制化开发的WooCommerce独立站商城在安全性、交易风险控制以及整体用户体验方面有显著优势。以下是定制化开发在这些方面的具体表现&#xff1a; 1. 安全性更高 定制化开发允许开发者从底层架构开始设计和优化&#xff0c;确保网站的安全性。以下是具体表现&#xff1a; (1…

CSS【实战】模拟 html 的 title 属性(鼠标悬浮显示提示文字)

效果 原理 提示内容的定位&#xff1a;子绝父相鼠标悬浮前&#xff0c;提示内容visibility: hidden;通过 :hover 触发鼠标悬浮样式&#xff0c;提示内容变为 visibility: visible; 代码 <!DOCTYPE html> <html lang"en"><head><meta charset&qu…

K8s控制器Deployment详解

回顾 ReplicaSet 控制器,该控制器是用来维护集群中运行的 Pod 数量的&#xff0c;但是往往在实际操作的时候&#xff0c;我们反而不会去直接使用 RS&#xff0c;而是会使用更上层的控制器&#xff0c;比如说 Deployment。 Deployment 一个非常重要的功能就是实现了 Pod 的滚动…

【MYSQL数据库异常处理】执行SQL语句报超时异常

MYSQL执行SQL语句异常&#xff1a;The last packet successfully received from the server was 100,107 milliseconds ago. The last packet sent successfully to the server was 100,101 milliseconds ago. 这个错误表明 MySQL 服务器与 JDBC 连接之间的通信超时了。通常由…

HJ C++11 Day2

Initializer Lists 对于一个类P class P{P(int a, int b){cout << "P(int, int), a" << a << ", b " << b << endl;}P(initializer_list<int> initlist){cout << "P(initializer_list<int>), val…

树莓派5首次开机保姆级教程(无显示器通过VNC连接树莓派桌面)

第一次开机详细步骤 步骤一&#xff1a;树莓派系统烧录1 搜索打开烧录软件“Raspberry Pi Imager”2 选择合适的设备、系统、SD卡3 烧录配置选项 步骤二&#xff1a;SSH远程树莓派1 树莓派插电2 网络连接&#xff08;有线或无线&#xff09;3 确定树莓派IP地址 步骤三&#xff…

装饰器模式--RequestWrapper、请求流request无法被重复读取

目录 前言一、场景二、原因分析三、解决四、更多 前言 曾经遇见这么一段代码&#xff0c;能看出来是把request又重新包装了一下&#xff0c;核心信息都不会改变 后面了解到这叫 装饰器模式&#xff08;Decorator Pattern&#xff09; &#xff1a;也称为包装模式(Wrapper Pat…

大语言模型进化论:从达尔文到AI的启示与展望

文章大纲 引言大语言模型中的“进化论”思想体现遗传变异过度繁殖和生存斗争大模型“过度繁殖”与“生存竞争”机制解析**一、过度繁殖:技术迭代的指数级爆发****二、生存竞争:计算资源的达尔文战场****三、生存竞争胜出关键要素****四、行业竞争格局演化趋势**核心结论自然选…

监听 RabbitMQ 延时交换机的消息数、OpenFeign 路径参数传入斜杠无法正确转义

背景 【MQ】一套为海量消息和高并发热点消息&#xff0c;提供高可用精准延时服务的解决方案 我现在有一个需求&#xff0c;就是监听 RabbitMQ 一个延时交换机的消息数&#xff0c;而 RabbitTemplate 是不存在对应的方法来获取的。 而我们在 RabbitMQ 的控制台却可以发现延时交…

分布式网络

分布式网络&#xff08;Distributed Network&#xff09;指的是一种计算机网络架构&#xff0c;其中计算资源&#xff08;计算、存储、数据处理等&#xff09;分布在多个物理或逻辑上的节点上&#xff0c;而不是集中在单一的服务器或数据中心中。这种架构的主要目标是提高系统的…

【Elasticsearch】Elasticsearch 中使用 HDFS 存储快照

在 Elasticsearch 中使用 HDFS 存储快照的步骤如下&#xff1a; 1.安装 HDFS 插件 要使用 HDFS 存储 Elasticsearch 的索引快照&#xff0c;需要在 Elasticsearch 集群的所有节点上安装 HDFS 插件。 • 在线安装&#xff1a;适用于网络环境良好的场景&#xff0c;执行以下命…

浅谈 DeepSeek 对 DBA 的影响

引言&#xff1a; 在人工智能技术飞速发展的背景下&#xff0c;DeepSeek 作为一款基于混合专家模型&#xff08;MoE&#xff09;和强化学习技术的大语言模型&#xff0c;正在重塑传统数据库管理&#xff08;DBA&#xff09;的工作模式。通过结合其强大的自然语言处理能力、推理…

STM32F4 UDP组播通信:填一填ST官方HAL库的坑

先说写作本文的原因&#xff0c;由于开项目开发中需要用到UDP组播接收的功能&#xff0c;但是ST官方没有提供合适的参考&#xff0c;使用STM32CubeMX生成的代码也是不能直接使用的&#xff0c;而我在网上找了一大圈&#xff0c;也没有一个能够直接解决的方案&#xff0c;deepse…