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

server/2025/3/10 3:38:11/

 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.ppmy.cn/server/173837.html

相关文章

linux服务器根据内核架构下载各种软件依赖插件(例子:Anolis服务器ARM64架构内核Nginx依赖插件下载)

Anolis服务器ARM64架构内核Nginx依赖插件下载 Nginxy依赖包&#xff1a;阿里云镜像站搜索自己的系统如下点击系统&#xff0c;进入详情页面点击下载地址点击对应版本号选择Os继续点击OS点击Packagesctrf搜索资源&#xff0c;依次下载资源&#xff0c;版本建议选最新把下载好的资…

鸿蒙跨平台框架ArkUI-X

01 引言 目前&#xff0c;移动端主流跨平台方案有Flutter、React Native、uni-app等等&#xff0c;还有刚推出不久的Compose-Multiplatform&#xff0c;真所谓是百花齐放。这些框架各有特点&#xff0c;技术实现各有差异&#xff0c;比如Flutter通过Dart编写的UI描述对接Flutte…

Hive-数据倾斜优化

数据倾斜的原因 1&#xff09;key分布不均匀&#xff0c;本质上就是业务数据有可能会存在倾斜 2&#xff09;某些SQL语句本身就有数据倾斜 关键词 情形 后果 Join A、其中一个表较小&#xff0c;但是key集中; B、两张表都是大表&#xff0c;key不均 分发到…

DeepSeek 助力 Vue3 开发:打造丝滑的表格(Table)示例3: 行选择

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…

蓝桥杯备赛:一道数学题(练思维(同余的应用))

题目&#xff1a;请问由1-8组成的8位数中有多少个数字可以被1111整除&#xff1f; 首先这道题目看着很难&#xff0c;如果我们直接用代码做的话&#xff0c;也要跑很久&#xff0c;那能不呢想想有什么样的思路可以巧妙一点解开这道题目呢&#xff1f; 有的兄弟有的 这道题目的…

fastapi+mysql实现问卷调查系统

说明&#xff1a; 我计划用fastapipython&#xff0c;实现多表查询&#xff0c;并写成接口&#xff0c;在postman里面请求访问 step1: -- 1. 问卷表 CREATE TABLE survey (id INT PRIMARY KEY AUTO_INCREMENT,title VARCHAR(255) NOT NULL,created_at TIMESTAMP DEFAULT CURREN…

Geo3D建筑材质切换+屋顶纹理

一、简介 基于Threejs开发封装建筑渲染管线&#xff0c;利用简单二维建筑矢量面轮廓程序化生成3D建筑&#xff0c;支持材质一键切换&#xff0c;支持多样化建筑墙面材质和屋顶材质&#xff0c;支持建筑透明&#xff0c;支持地形高程适配&#xff0c;支持按空间范围裁剪挖洞等。…

力扣热题 100:二叉树专题经典题解析(前8道)

文章目录 一、二叉树的中序遍历&#xff08;题目 94&#xff09;1. 题目描述2. 示例3. 解题思路4. 代码实现&#xff08;Java&#xff09;5. 复杂度分析 二、二叉树的最大深度&#xff08;题目 104&#xff09;1. 题目描述2. 示例3. 解题思路4. 代码实现&#xff08;Java&#…