数据结构七:线性表之链式栈的设计

         在上篇博客,学习了用数组实现链的顺序存储结构,那是否存在用单链表实现栈的链式存储结构,答案是当然的,相比于顺序栈,用数组实现的栈效率很高,但若同时使用多个栈,顺序栈将浪费很多空间。用单链表来实现栈可避免这个问题,其代价是要为每个栈元素分配一个额外的指针空间(存放指针域),本篇博客详细总结用链表实现栈的链式存储结构,我们称之为链式栈。

目录

一、链式栈的接口函数实现

1.0  链式栈的概念和结构

1.0.0 链式栈的概念

1.0.1 链式栈结构体设计

1.1 链式栈的接口函数

1.2  链式栈的初始化

1.3 入栈(相当于单链表的头插)

1.4 出栈(相当于单链表的头删)

1.5 获取栈顶元素值

1.6 获取有效元素个数

1.7 判空

1.8 打印

1.9 清空

1.10 销毁

二、总结


一、链式栈的接口函数实现

1.0  链式栈的概念和结构

1.0.0 链式栈的概念

         链式栈,即用链表实现栈式存储结构。为保证栈的数据元素的后进先出的高效性,那么我们将栈顶应该设计在数组的哪一端呢?学过单链表,我们可以知道,单链表的头插和头删的效率非常高,时间复杂度为O(1),因此,参考单链表,我们可以将栈顶设计为第一个有效结点,这样入栈和出栈的时间复杂度都可以达到O(1)

1.0.1 链式栈结构体设计

       经过上面的分析,我们知道,链式栈就是将栈顶设计为第一个有效结点的单链表,本质上就是只能进行头插和头删的单链表,本质和单链表的设计差不多,主要为2个成员,数据域和指针域。在之前的单链表中,我们把头结点和有效结点设计为一样的结构,浪费掉头结点的数据域,因此只设计成一个结构体类型,在链式栈这里,为了巩固练习,我们将头结点和有效结点分开设计,并且使用头结点的数据域,用来存放链式栈的有效元素个数,因此结构如下:

typedef int ELEM_TYPE;//有效结点的设计:数据域和指针域
typedef struct Stack_Node
{ELEM_TYPE data;struct Stack_Node *next;
}Stack_Node, *PStack_Node;//头结点的设计:存放有效结点个数的数据域和指针域
typedef struct Link_Stack
{int count;struct Stack_Node *top;
}Link_Stack, *PLink_Stack;
 

1.1 链式栈的接口函数

       链式栈的基本操作有:初始化,头插,尾插,按位置插,头删,尾删,按位置删,查找,按值删,获取有效值个数,判空,清空,销毁,打印。这里详细展示这些基本操作的实现思想和画图分析以及代码实现和算法效率分析,

      注意:链式栈与单链表相同,由于它是按需索取,因此,不需要进行判满和扩容操作;

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "stack_list.h"//初始化
void Init_stack_list(struct Link_Stack* lst);//入栈
void Push_list(struct Link_Stack* lst, ELEM_TYPE val);//出栈
void Pop_list(struct Link_Stack* lst);//获取栈顶元素值
ELEM_TYPE Top_list(struct Link_Stack* lst);//获取有效元素个数
int Get_length_list(struct Link_Stack* lst);//判空
bool IsEmpty_list(struct Link_Stack* lst);//打印
void Show_list(struct Link_Stack* lst);//清空
void Clear_list(struct Link_Stack* lst);//销毁
void Destroy_list(struct Link_Stack* lst);

1.2  链式栈的初始化

    链式栈在创建以后必须要进行初始化,否则内部为随机值,无法使用。链式栈的初始化主要是对结构体成员赋初值。

//初始化
void Init_stack_list(struct Link_Stack* lst)
{第0步:传入的指针参数检测assert(lst!=NULL);第1步:对指针域赋初值,数据域赋初值lst->top = NULL;lst->count = 0;   //头结点的数据域使用,最开始没有有效结点,赋初值为0}

1.3 入栈(相当于单链表的头插)

  入栈相当于是单链表的头插,基本思路如下:

    第0步:assert对传入的指针检测;

    第1步:购买新节点(购买好节点之后,记得将val值赋值进去);

    第2步:找到合适的插入位置;

    第3步:插入注意核心代码,先牵右手,再牵左手!!!否则会发生内存泄漏。

//入栈
void Push_list(struct Link_Stack* lst, ELEM_TYPE val)
{第0步:传入的指针参数检测assert(lst!=NULL);//1.购买新节点struct Stack_Node* pnewnode = (struct Stack_Node *)malloc(1*sizeof(struct Stack_Node));pnewnode->data = val;//2.找到合适的插入位置 头插不用管//3.插入pnewnode->next = lst->top;lst->top = pnewnode;lst->count++;
}

1.4 出栈(相当于单链表的头删)

      出栈相当于单链表的头删操作 ,对于删除操作,则需要对链表进行判空操作!并且删除操作遵循基本同样的4个步骤,需要理解加记忆。删除操作的基本思路如下:

①:用指针q指向待删除节点;
②:用指针p指向待删除节点的前驱节点;(头删的话,这里p可以被plist代替)
③:跨越指向;
④:释放待删除节点。

//出栈
void Pop_list(struct Link_Stack* lst)
{第0步:传入的指针参数检测assert(lst!=NULL);//1.判空if(IsEmpty_list(lst)){return;}//2.用q指向待删除节点, 用p指向待删除节点的上一个节点struct Stack_Node *q = lst->top;//3.跨越指向+释放 count--lst->top = q->next;free(q);q = NULL;lst->count--;}

1.5 获取栈顶元素值

链式栈中,栈顶就是第一个有效结点,因此只需要返回第一个有效结点的数据域即可。


//获取栈顶元素值
ELEM_TYPE Top_list(struct Link_Stack* lst)
{第0步:传入的指针参数检测assert(lst!=NULL);第1步:判空if(IsEmpty_list(lst)){exit(1);}第2步:直接返回第一个有效结点的数据域return lst->top->data;
}

1.6 获取有效元素个数

头结点的数据域就是用来记录有效结点个数的,直接返回即可

//获取有效元素个数
int Get_length_list(struct Link_Stack* lst)
{第0步:传入的指针参数检测assert(lst!=NULL);第1步:头结点的数据域就是用来记录有效结点个数的,直接返回即可return lst->count;
}

1.7 判空

    直接判断头结点的指针域是否为空即可!

//判空
bool IsEmpty_list(struct Link_Stack* lst)
{第0步:传入的指针参数检测assert(lst!=NULL);第1步:没有有效结点时,头结点的指针域为空return lst->top == NULL;
}

1.8 打印

直接从第一个有效节点开始,从前往后遍历,打印有效结点的数据域即可

//打印
void Show_list(struct Link_Stack* lst)
{第0步:传入的指针参数检测assert(lst!=NULL);第1步:直接从第一个有效节点开始,从前往后遍历,打印有效结点的数据域即可struct Stack_Node *p = lst->top;for(; p!=NULL; p=p->next){printf("%d ", p->data);}printf("\n");
}

1.9 清空

链式栈和单链表一样,清空和销毁完全一样

//清空
void Clear_list(struct Link_Stack* lst)
{Destroy_list(lst);
}

1.10 销毁

链式栈的销毁同样有两种方式,这里只写第2种即可。

//销毁
void Destroy_list(struct Link_Stack* lst)
{第0步:传入的指针参数检测assert(lst!=NULL);第1步:利用双指针,依次从前往后删除struct Stack_Node *q = lst->top;struct Stack_Node *p = NULL;lst->count = 0;lst->top = NULL;while(q != NULL){p = q->next;free(q);q=NULL;q = p;}
}

二、总结

      从以上分析,我们可以知道链式栈只是单链表的一种特殊情况,限定了数据元素的插入和删除位置,链式栈是一种基于单链表实现的栈,在空间复杂度上,顺序栈在创建时就申请了数组空间,若栈经常处于不满状态将造成存储空间的浪费;链式栈所需空间是根据需要随时申请的,比之顺序栈仅仅是其每个结点需要额外的空间以存储其next指针域。在时间复杂度上,对于针对栈顶的基本操作(压入、弹出和栈顶元素存取),容易看出,顺序栈和链式栈的时间复杂性均为O(1) 。

       它具有一些优点:

  1. 动态性能好: 链式栈采用链式存储结构,可以动态地分配内存空间,而顺序栈则需要预先分配一定大小的连续内存空间。因此,链式栈能够根据实际需求动态扩展或缩减内存,不会受到固定大小的限制。

  2. 灵活性强: 由于链式栈的内存空间是动态分配的,因此在入栈和出栈操作时不需要移动元素,而是通过修改指针来实现。这使得链式栈的操作效率更高,并且不会出现栈满的情况,避免了顺序栈可能出现的溢出问题。

      以上便是我为大家带来的链式栈设计内容,若有不足,望各位大佬在评论区指出,谢谢大家!下一节继续进行链式栈的内容,感兴趣的你可以留下你们的点赞、收藏和关注,这是对我极大的鼓励,我也会更加努力创作更优质的作品。再次感谢大家!

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

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

相关文章

ThinkPHP Lang多语言本地文件包含漏洞(QVD-2022-46174)漏洞复现

1 漏洞描述 ThinkPHP是一个在中国使用较多的PHP框架。在其6.0.13版本及以前&#xff0c;存在一处本地文件包含漏洞。当ThinkPHP开启了多语言功能时&#xff0c;攻击者可以通过lang参数和目录穿越实现文件包含&#xff0c;当存在其他扩展模块如 pear 扩展时&#xff0c;攻击者可…

高级IO|从封装epoll服务器到实现Reactor服务器|Part1

从封装epoll_server到实现reactor服务器(part1) 项目复习&#xff1a;从封装epoll_server到实现reactor服务器(part1)EPOLL模式服务器初步 select, poll, epoll的优缺点epoll的几个细节封装epoll_server基本框架先写好创建监听套接字和创建epoll模型可以Accept了吗&#xff1f…

《架构风清扬-Java面试系列第25讲》聊聊ArrayBlockingQueue的特点及使用场景

ArrayBlockingQueue是BlockingQueue接口的一个实现类之一 这个属于基础性问题&#xff0c;老规矩&#xff0c;我们将从使用场景和代码示例来进行讲解 来&#xff0c;思考片刻&#xff0c;给出你的答案 1&#xff0c;使用场景 实现&#xff1a;基于数组实现的有界阻塞队列&…

Stability AI 推出稳定音频 2.0:为创作者提供先进的 AI 生成音频 - Circle 阅读助手

概述 Stability AI 的发布再次突破了创新的界限。这一尖端模型以其前身的成功为基础&#xff0c;引入了一系列突破性的功能&#xff0c;有望彻底改变艺术家和音乐家创建和操作音频内容的方式。 Stable Audio 2.0 代表了人工智能生成音频发展的一个重要里程碑&#xff0c;为质量…

mysql UNION 联合查询

mysql UNION 联合查询 业务需要拉数据&#xff0c;这里需要对查询不同格式的数据进行组装&#xff0c;此处采用联合查询 注意1&#xff1a;null as 设备关爱 &#xff0c;结果为null&#xff0c;表头为设备关爱 注意2&#xff1a; UNION 或者 UNION ALL 联合查询自行选用 注意3…

如何避免被恶意攻击的IP地址

随着互联网的普及和发展&#xff0c;网络安全问题日益受到关注&#xff0c;恶意攻击成为网络安全的一大威胁。而IP地址作为网络通信的基础&#xff0c;常常成为恶意攻击的目标之一。本文将探讨如何避免被恶意攻击的IP地址&#xff0c;提高网络安全水平。 1. 定期更新安全补丁 …

【kettle003】kettle访问SQL Server数据库并处理数据至execl文件

一直以来想写下基于kettle的系列文章&#xff0c;作为较火的数据ETL工具&#xff0c;也是日常项目开发中常用的一款工具&#xff0c;最近刚好挤时间梳理、总结下这块儿的知识体系。 熟悉、梳理、总结下Microsoft SQL Server 2022关系数据库相关知识体系 kettle访问SQL Server数…

ITMS-90426: Invalid Swift Support

原文 Please correct the following issues and upload a new binary to App Store Connect. ITMS-90426: Invalid Swift Support - The SwiftSupport folder is missing. Rebuild your app using the current public (GM) version of Xcode and resubmit it. 解决方式 ITMS-…

uniapp小程序订阅通知

服务 开通订阅服务 const tmplIds ref([tsdasdadasdfgdrtwexQHdEsjZV])//换成自己的 function confirm(){uni.requestSubscribeMessage({tmplIds: tmplIds.value,success: (res) > {// console.log(res)let auth_notice res[tmplIds.value[0]] accept ? 1 : 2 //1是接…

在Redux Toolkit中使用redux-persist进行状态持久化

在 Redux Toolkit 中使用 redux-persist 持久化插件的步骤如下: 安装依赖 npm install redux-persist配置 persistConfig 在 Redux store 配置文件中(例如 rootReducer.js)&#xff0c;导入必要的模块并配置持久化选项: import { combineReducers } from redux; import { p…

MIT 6.172 笔记 现代硬件算法案例分析

本文是https://en.algorithmica.org/hpc/和MIT 6.172的课后题解析 课程地址&#xff1a; 文章目录 HW2 Profiling Serial Merge Sort测试DEBUG和非DEBUG区别测试inline和非inline区别Coarsening HW3 向量化为什么用负偏移量测量向量化跨步向量化 HW4 Reducer Hyperobjects比较o…

[Qt的学习日常]--信号和槽

前言 作者&#xff1a;小蜗牛向前冲 名言&#xff1a;我可以接受失败&#xff0c;但我不能接受放弃 如果觉的博主的文章还不错的话&#xff0c;还请点赞&#xff0c;收藏&#xff0c;关注&#x1f440;支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 本期学习&#xff…

【JAVA】一文掌握Java并发编程

Java 开发中&#xff0c;并发编程属于相当重要的一个知识点&#xff0c;可以说&#xff0c;Java 的并发能力&#xff0c;是成就今日 Java 地位的因素之一。Java 的并发编程由浅入深实质上是包含 Java&#xff08;API&#xff09;层、JVM&#xff08;虚拟机&#xff09;层、内核…

[Linux][网络][网络编程套接字][一][预备知识][套接字地址结构]详细讲解

目录 0.预备知识1.理解源IP地址和目的IP地址2.理解源MAC地址和目的MAC地址3.端口号4.理解端口号和进程ID5.理解源端口号和目的端口号6.通过IP地址、端口号、协议号进行通信识别7.认识TCP协议和UDP协议8.网络字节序 1.套接字地址结构(sockaddr) 0.预备知识 1.理解源IP地址和目的…

redisson分布式锁的单机版应用

package com.redis;/*** author linn* date 2024年04月23日 15:31*/ import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.springframework.context.annotation.Bean; import org.springframework.context.…

从 0 到 1 创建、测试并发布属于自己的 Go 开源库

作者&#xff1a;陈明勇 个人网站&#xff1a;https://chenmingyong.cn 文章持续更新&#xff0c;如果本文能让您有所收获&#xff0c;欢迎点赞收藏加关注本号。 微信阅读可搜《程序员陈明勇》。 这篇文章已被收录于 GitHub https://github.com/chenmingyong0423/blog&#xff…

AIGC——什么是人工智能生成内容

人工智能生成内容&#xff08;AIGC&#xff09;是当今数字时代的一个引人注目的前沿技术&#xff0c;它借助深度学习和自然语言处理等技术&#xff0c;使计算机系统具备了生成高质量文本、图像、音频等多媒体内容的能力。AIGC的出现不仅推动了信息技术的发展&#xff0c;也在多…

判断字符串由几个单词组成(C语言)

一、N-S流程图&#xff1b; 二、运行结果&#xff1b; 三、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;int world 0;int i 0;char c 0;char string[81] { 0 };int num 0;//提示用户&#xff…

C++---重载

1、运算符重载 #include <iostream> using namespace std; class complex { int rel; int vir; public: complex(){} complex(int rel,int vir):rel(rel),vir(vir){} void show() { cout << rel << "" << vir << "i" <&l…

(待更)DRF: 序列化器、View、APIView、GenericAPIView、Mixin、ViewSet、ModelViewSet的源码解析

前言&#xff1a;还没有整理&#xff0c;后续有时间再整理&#xff0c;目前只是个人思路&#xff0c;文章较乱。 注意路径匹配的“/” 我们的url里面加了“/”&#xff0c;但是用apifox等非浏览器的工具发起请求时没有加“/”&#xff0c;而且还不是get请求&#xff0c;那么这…