数据结构(链栈——c语言实现)

embedded/2024/11/24 4:07:00/

      链式栈(Linked Stack)是一种基于链表数据结构实现的栈。它利用链表节点的指针来存储元素,并通过指针的链接关系来维护栈的后进先出(LIFO, Last In First Out)特性。

链式栈的优点

  1. 动态大小

           链式栈的大小可以动态调整,不需要在创建时指定栈的最大容量。当栈满时,只需创建一个新的节点并将其链接到栈顶即可。
  2. 内存效率高

           链式栈只在需要时分配内存,不需要像数组那样预先分配一大块连续的内存空间。因此,它更适合于内存分配困难的嵌入式系统或内存碎片较多的环境。
  3. 避免内存浪费

           链式栈在元素弹出(pop)时,可以释放已使用节点的内存,避免内存浪费。而数组实现的栈在栈收缩时通常不能立即释放未使用的内存。
  4. 操作灵活性

           链式栈在栈顶和栈底的插入和删除操作都相对高效,因为这些操作只涉及指针的修改,不需要移动大量数据。
  5. 易于实现复杂操作

           由于链式栈的节点是独立的,可以很容易地实现一些复杂的栈操作,如栈的合并、分割、复制等。
  6. 无需初始化大量空间

           在实际应用中,栈的大小往往难以预估。链式栈可以根据需要动态增长,而不需要在初始化时分配过多的空间。
  7. 减少栈溢出风险

                在一些应用场景中,栈的大小可能会非常大,数组实现的栈可能会遇到栈溢出的问题。           链式栈由于动态分配内存,可以有效减少这种风险。  

链栈 

       链式栈的实现和链表类似,都是用指针来保存下一个节点的地址,在这里也是约定头节点不存数据;因为栈只能在一端进行操作,因此我们以头结点的位置来进行压栈和弹栈操作。

#ifndef _LINKSTACK_H
#define _LINKSTACK_H#include <stdio.h>
#include <stdlib.h>
#include <string.h>typedef char Type;
typedef struct link{Type data; struct link *rear;  //存储下一个节点地址
}lstack;//创建栈
lstack *create_lstack();
//判空 空为0非空为1
int empty_lstack(lstack *l);
//求长度
int length_lstack(lstack *l);
//压栈
void push_lstack(lstack *l,Type data);
//弹栈
Type pop_lstack(lstack *l);
//取栈顶元素
Type get_lstack(lstack *l);
//初始化
void init_lstack(lstack *l);
//回收
void free_lstack(lstack **l);#endif

        在创建结构体的时候,结构体名也是不能省略,因为我们在结构体里面需要使用结构体 *类型的指针来指向下一个节点;结构体成员就两个,一个是用来保存数据的变量data,另外一个是结构体指针,相当于链。

#include "linkstack.h"//创建栈
lstack *create_lstack()
{lstack *l = (lstack *)malloc(sizeof(lstack));if(NULL == l){perror("create lstack malloc");return NULL;}l->rear = NULL;return l;
}
//判空 空为0非空为1
int empty_lstack(lstack *l)
{if(NULL == l){puts("lstack is NULL");return -1;}if(l->rear){return 1;}else{puts("stack is empty");return 0;}
}
//求长度
int length_lstack(lstack *l)
{if(NULL == l){puts("lstack is NULL");return -1;}int n = 0;while(l->rear){n++;l = l->rear;}return n;
}
//压栈
void push_lstack(lstack *l,Type data)
{if(NULL == l){puts("lstack is NULL");return;}lstack *s = (lstack *)malloc(sizeof(lstack));s->data = data;s->rear = l->rear;l->rear = s;
}
//弹栈
Type pop_lstack(lstack *l)
{if(empty_lstack(l) == 1){lstack *s = l->rear;l->rear = s->rear;Type d = s->data;free(s);return d;}
}
//取栈顶元素
Type get_lstack(lstack *l)
{if(empty_lstack(l) == 1){return l->rear->data;}
}
//初始化
void init_lstack(lstack *l)
{if(NULL == l){puts("lstack is NULL");return;}while(l->rear){lstack *s = l->rear;l->rear = s->rear;free(s);}
}
//回收
void free_lstack(lstack **l)
{if(NULL == l){puts("lstack is NULL");return;}init_lstack(*l);free(*l);*l = NULL;
}

创建栈: lstack *create_lstack()

       创建同样是在堆区开辟空间,开辟空间之后判断开辟是否成功,成功之后让里面的指针rear指向NULL,头节点不存数据,因此不需要给data赋值,最后返回这个开辟空间的地址。

判空: int empty_lstack(lstack *l)

       既然我们操作的是头节点,因此判空只需要判断头节点的下一个是否为空就能知道栈是否为空,空栈函数返回0,非空函数返回1。

求长度:int length_lstack(lstack *l)

       求长度其实就和遍历一样,使用一个循环来遍历这个栈,然后用一个变量来计数,最后返回这个计数的值即可。

压栈:void push_lstack(lstack *l,Type data)

       压栈其实就和链表的头插一样,先创建节点,创建完成之后给data赋值,然后让新节点的rear指向头节点的下一个,最后让头节点的rear指向新节点即可。

弹栈:Type pop_lstack(lstack *l)

       弹栈操作和压栈正好相反,我们先使用一个指针来接收要删除的节点,然后把原来头节点与这个节点之间的链断开,接着让头结点的rear指向要删除节点的下一个,最后将空间释放即可;在这里弹栈我们不止要把这个元素弹出来,我们还需要使用一个变量来接收弹出来的这个元素,让它作为弹栈函数的返回值。

获取栈顶元素:Type get_lstack(lstack *l)

       获取栈顶元素也就是将头节点的下一个节点里面的data的值作为函数的返回值直接反出来即可。

初始化:void init_lstack(lstack *l)

        链栈的初始化与链表是一样的,首先循环遍历,在每次循环时定义一个指针接收节点,将节点空间释放掉;将除了头节点以外的节点都释放掉,这就是链式栈的初始化。

回收:void free_lstack(lstack **l)

       链式栈的回收函数传参同样是传二级指针,这里需要拿到链式栈头节点的指针地址,现将链式栈初始化,最后再把头节点空间释放掉,最后让指向头结点的指针指向NULL即可。

测试(主函数)

#include "linkstack.h"
int main(int agrc,char *agrv[])
{lstack *s = create_lstack();empty_lstack(s);puts("压栈,压入a");push_lstack(s,'a');puts("压栈,压入b");push_lstack(s,'b');puts("压栈,压入A");push_lstack(s,65);printf("此时链栈的长度为:%d\n",length_lstack(s));printf("取此时栈顶元素为:%c\n",get_lstack(s));puts("弹栈");pop_lstack(s);//puts("弹栈");//pop_lstack(s);printf("取此时栈顶元素为:%c\n",get_lstack(s));printf("此时链栈的长度为:%d\n",length_lstack(s));puts("初始化栈链");init_lstack(s);printf("此时链栈的长度为:%d\n",length_lstack(s));free_lstack(&s);printf("s=%p\n",s);return 0;
}


http://www.ppmy.cn/embedded/140025.html

相关文章

Oracle数据库安全扫描1158/3938端口出现弱SSL加密算法解决方法之一

问题复述 某国企项目现场反应安全扫描出部署某历史项目的Windows服务器上的1158及3938两个端口出现了弱SSL加密算法漏洞&#xff0c;要求整改。 经过核实&#xff0c;该Windows服务器上部署了tomcat与Oracle 11g数据库&#xff0c;其中1158和3938两个端口均为Oracle数据库所使…

使用Python和OpenCV连接并处理IP摄像头视频流

使用Python和OpenCV连接并处理IP摄像头视频流 随着智能设备的发展&#xff0c;越来越多的家庭和企业开始使用IP摄像头进行安全监控或远程查看。这些摄像头通常可以通过网络访问&#xff0c;提供了丰富的功能&#xff0c;如实时视频流、云台控制等。本文将详细介绍如何利用Pyth…

设计模式之 桥接模式

桥接模式&#xff08;Bridge Pattern&#xff09;是一种结构型设计模式&#xff0c;其核心思想是将抽象部分和实现部分分离&#xff0c;使它们可以独立地变化。通过桥接模式&#xff0c;抽象部分和实现部分可以独立扩展&#xff0c;从而避免了继承层次过深和高耦合的问题。 桥…

趋势洞察|AI 能否带动裸金属 K8s 强势崛起?

随着容器技术的不断成熟&#xff0c;不少企业在开展私有化容器平台建设时&#xff0c;首要考虑的问题就是容器的部署环境——是采用虚拟机还是物理机运行容器&#xff1f;在往期“虚拟化 vs. 裸金属*”系列文章中&#xff0c;我们分别对比了容器部署在虚拟化平台和物理机上的架…

使用 OpenAI 进行数据探索性分析(EDA)

探索性数据分析&#xff08;Exploratory Data Analysis, 简称 EDA&#xff09;是数据分析中不可或缺的环节&#xff0c;帮助分析师快速了解数据的分布、特征和潜在模式。传统的 EDA 通常需要手动编写代码或使用工具完成。现在&#xff0c;通过 OpenAI 的 GPT-4 模型&#xff0c…

【手写一个spring】spring源码的简单实现--容器启动

文章目录 前言applicationContext初始化的前置操作获取扫描路径判断是否是bean对象 前言 今天开启一个新的章节,手写一个简易版的spring,帮助大家对spring能有一个更深层次的理解. 我将分为以下几个章节进行学习: 今天先开启我们的第一个章节:容器启动. applicationContext初…

深度学习:神经网络中的非线性激活的使用

深度学习&#xff1a;神经网络中的非线性激活的使用 在神经网络中&#xff0c;非线性激活函数是至关重要的组件&#xff0c;它们使网络能够捕捉和模拟输入数据中的复杂非线性关系。这些激活函数的主要任务是帮助网络解决那些无法通过简单的线性操作&#xff08;如权重相乘和偏…

django基于django的民族服饰数据分析系统的设计与实现

摘 要 随着网络科技的发展&#xff0c;利用大数据分析对民族服饰进行管理已势在必行&#xff1b;该平台将帮助企业更好地理解服饰市场的趋势&#xff0c;优化服装款式&#xff0c;提高服装的质量。 本文讲述了基于python语言开发&#xff0c;后台数据库选择MySQL进行数据的存储…