NzN的数据结构--栈的实现

         在前面我们已经学习了哪些线性数据结构呢?大家一起来回顾一下:C语言学过的数组,数据结构中的线性表和顺序表和链表。那我们今天再来介绍数据结构里的两个线性结构--栈和队列。

目录

一、栈的概念及结构 

二、用数组实现栈

1. 栈的初始化和销毁

2. 从栈顶插入数据

3. 删除栈顶元素

4. 取栈顶元素

5. 计算栈中元素的数量

6. 判断栈是否为空

三、用链表实现栈

四、两种实现对比

1. 支持操作

2. 时间效率

3. 空间效率

五、栈的应用


一、栈的概念及结构 

        栈:一种特殊的线性表,只允许在固定一端插入和删除元素。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。

        压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶

        出栈:栈的删除操作叫做出栈,出数据也在栈顶

二、用数组实现栈

        栈的实现一般可以使用数组或链表实现,相对而言数组的结构实现更优一些

         使用数组实现栈时,我们可以将数组的尾部作为栈顶。入栈与出栈操作分别对应在数组尾部添加元素与删除元素。那我们先通过数组实现栈。

        先在头文件中定义需要实现的功能接口:

//Stack.h
#pragma once
#include<stdio.h>
#include <stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int STDataType;
typedef struct Stack
{STDataType* a;//数组实现int top;//栈顶int capacity;//栈的容量
}ST;
//栈的初始化和销毁
void STInit(ST* ps);
void STDestroy(ST* ps);
//插入(只能从栈顶插入)
void STPush(ST* ps, STDataType x);
//栈中元素的删除
void STPop(ST* ps);
//取栈顶元素
STDataType STTop(ST* ps);
//计算栈中元素的数量
int STSize(ST* ps);
//判断栈是否为空
bool STEmpty(ST* ps);

1. 栈的初始化和销毁

void STInit(ST* ps)
{assert(ps);ps->a = NULL;ps->top = 0;//top=0指向的是栈顶元素的下一个//我们也可以让top=-1,这样top就指向栈顶元素ps->capacity = 0;
}
void STDestroy(ST* ps)
{assert(ps);free(ps->a);ps->a = NULL;ps->top = 0;ps->capacity = 0;
}

2. 从栈顶插入数据

void STPush(ST* ps, STDataType x)
{assert(ps);//空间不够直接扩容if (ps->top == ps->capacity){//一开始没有给容量多大,因此需要判断int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//ps->a为空的话,realloc相当于mallocSTDataType* tmp = (STDataType*)realloc(ps->a, newcapacity * sizeof(STDataType));if (tmp == NULL){perror("realloc");exit(1);}ps->a = tmp;ps->capacity = newcapacity;}ps->a[ps->top] = x;ps->top++;
}

3. 删除栈顶元素

void STPop(ST* ps)
{assert(ps);assert(!STEmpty(ps));ps->top--;
}

4. 取栈顶元素

STDataType STTop(ST* ps)
{assert(ps);assert(!STEmpty(ps));return ps->a[ps->top - 1];
}

5. 计算栈中元素的数量

int STSize(ST* ps)
{assert(ps);return ps->top;
}

6. 判断栈是否为空

bool STEmpty(ST* ps)
{assert(ps);return ps->top == 0;
}

三、用链表实现栈

        使用链表实现栈时,我们可以将链表的头节点视为栈顶,尾节点视为栈底。

        对于入栈操作,我们只需将元素插入链表头部,这种节点插入方法被称为“头插法”。而对于出栈操作,只需将头节点从链表中删除即可。

         实现逻辑与数组相似,因此在这里直接给出实现代码:

//基于链表实现的栈
typedef struct {ListNode *top;//将头节点作为栈顶int size;//栈的长度
} LinkedListStack;//栈的初始化
LinkedListStack *newLinkedListStack() 
{LinkedListStack *s = malloc(sizeof(LinkedListStack));s->top = NULL;s->size = 0;return s;
}//栈的销毁
void delLinkedListStack(LinkedListStack *s) 
{while (s->top) {ListNode *n = s->top->next;free(s->top);s->top = n;}free(s);
}//计算栈中元素的数量
int size(LinkedListStack *s) 
{return s->size;
}//判断栈是否为空
bool isEmpty(LinkedListStack *s) 
{return size(s) == 0;
}//从栈顶插入数据
void push(LinkedListStack *s, int num) 
{ListNode *node = (ListNode *)malloc(sizeof(ListNode));node->next = s->top;//更新新加节点指针域node->val = num;//更新新加节点数据域s->top = node;//更新栈顶s->size++;//更新栈大小
}//取栈顶元素
int peek(LinkedListStack *s) {if (s->size == 0) {printf("栈为空\n");return -1;}return s->top->val;
}//删除栈顶元素
int pop(LinkedListStack *s) 
{int val = peek(s);ListNode *tmp = s->top;s->top = s->top->next;//释放内存free(tmp);s->size--;return val;
}

四、两种实现对比

1. 支持操作

        两种实现都支持栈定义中的各项操作。数组实现额外支持随机访问,但这已超出了栈的定义范畴,因此一般不会用到。

2. 时间效率

在基于数组的实现中,入栈和出栈操作都在预先分配好的连续内存中进行,具有很好的缓存本地性,因此效率较高。然而,如果入栈时超出数组容量,会触发扩容机制,导致该次入栈操作的时间复杂度变为O(N) 。

在基于链表的实现中,链表的扩容非常灵活,不存在上述数组扩容时效率降低的问题。但是,入栈操作需要初始化节点对象并修改指针,因此效率相对较低。不过,如果入栈元素本身就是节点对象,那么可以省去初始化步骤,从而提高效率。

        综上所述,当入栈与出栈操作的元素是基本数据类型时,例如int或double我们可以得出以下结论。

  • 基于数组实现的栈在扩容时效率会降低,但由于扩容是低频操作,因此平均效率更高。
  • 基于链表实现的栈可以提供更加稳定的效率表现。

3. 空间效率

        在初始化列表时,系统会为列表分配“初始容量”,该容量可能超出实际需求;并且,扩容机制通常是按照特定倍率(例如 2 倍)进行扩容的,扩容后的容量也可能超出实际需求。因此,基于数组实现的栈可能造成一定的空间浪费

        然而,由于链表节点需要额外存储指针,因此链表节点占用的空间相对较大

        综上,我们不能简单地确定哪种实现更加节省内存,需要针对具体情况进行分析。

五、栈的应用

  • 浏览器中的后退与前进、软件中的撤销与反撤销。每当我们打开新的网页,浏览器就会对上一个网页执行入栈,这样我们就可以通过后退操作回到上一个网页。后退操作实际上是在执行出栈。如果要同时支持后退和前进,那么需要两个栈来配合实现。
  • 程序内存管理。每次调用函数时,系统都会在栈顶添加一个栈帧,用于记录函数的上下文信息。在递归函数中,向下递推阶段会不断执行入栈操作,而向上回溯阶段则会不断执行出栈操作。

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

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

相关文章

Windows系统Docker部署IT工具箱It- Tools结合内网穿透实现公网访问

文章目录 1. 使用Docker本地部署it-tools2. 本地访问it-tools3. 安装cpolar内网穿透4. 固定it-tools公网地址 本篇文章将介绍如何在Windows上使用Docker本地部署IT- Tools&#xff0c;并且同样可以结合cpolar实现公网访问。 在前一篇文章中我们讲解了如何在Linux中使用Docker搭…

node.js-入门

定义 Node.js是一个跨平台Javascript运行环境&#xff0c;使开发者可以搭建服务器端的Javascript应用程序 作用&#xff1a;使用Node.js编写服务器端程序 1&#xff09;编写数据接口&#xff0c;提供网页资源浏览功能等 2&#xff09;前端工程化&#xff1a;集成各种开发中…

[C++]map set

一、set 1、概念 set是按照一定次序存储元素的容器在set中&#xff0c;元素的value也标识它(value就是key&#xff0c;类型为T)&#xff0c;并且每个value必须是唯一的。set中的元素不能在容器中修改(元素总是const)&#xff0c;但是可以从容器中插入或删除它们。在内部&…

4.9号驱动

1. ARM裸机开发和Linux系统开发的异同 相同点&#xff1a;都是对硬件进行操作 不同点&#xff1a; 有无操作系统 是否具备多进程多线程开发 是否可以调用库函数 操作地址是否相同&#xff0c;arm操作物理地址&#xff0c;驱动操作虚拟地址 2. Linux操作系统的层次 应用层…

在vite中限制node版本

1.修改package.json文件 {"name": "wine-store-frontend","version": "0.0.0","private": true,"type": "module","scripts": {"dev": "vite --open","build"…

Ant Design Vue 表单验证手机号的正则

代码&#xff1a; pattern: /^1[3456789]\d{9}$/ 1. <a-form-item label"原手机号" v-bind"validateInfos.contactTel"><a-inputstyle"width: 600px"allow-clear:maxlength"20"placeholder"请输入原手机号"v-mo…

【Android】 四大组件详解之活动、服务

文章目录 前言活动概述活动间的通信活动的生命周期活动的启动模式 服务概述启动和停止服务活动和服务间的通信服务的生命周期使用前台服务使用IntentService 前言 众所周知&#xff0c;Android的四大组件包括&#xff1a;活动&#xff08;Activity&#xff09;、服务&#xff…

东方博宜 1738. 胜负对决

东方博宜 1738. 胜负对决 以为这道题很简单呢&#xff0c;结果提交两次还不对&#xff0c;气死个人~ 思路&#xff1a;这道题的重点在于看清楚题意&#xff0c;是第奇数个&#xff0c;而不是数是奇数 。 还有&#xff0c;如果按照位数的奇偶来判定&#xff0c;那在读取数组的时…

UltraLight VM-UNet:平行视觉 Mamba 显着减少皮肤病变分割参数

UltraLight VM-UNet&#xff1a;平行视觉 Mamba 显着减少皮肤病变分割参数 摘要IntroductionRelated WorkMethodArchitecture OverviewMamba Parameter Impact AnalysisSkip-connection Path UltraLight VM-UNet: Parallel Vision MambaSignificantly Reduces Parameters for S…

【考研数学】强化跟张宇还是武忠祥?看完这篇就明白了

SO&#xff0c;不管你是选择武忠祥老师还是张宇老师&#xff0c;学长学姐都可以拍着胸脯告诉你&#xff0c;这两位老师都很优秀&#xff0c;跟哪位老师都能学好&#xff0c;所以你也不用担心因为错过一位老师的课而错失整个天下啦~ 根据不同老师的特点&#xff0c;结合自身情况…

RN使用蓝牙扫描

我项目需要用到蓝牙模块,蓝牙扫描到设备并且获取到电量显示到页面上,因此我做了如下demo,使用了react-native-ble-plx这个插件 点击进入官方文档官方文档 1.安卓环境配置(ios暂定,还没做ios,不过下面的方法是兼容的,自行配置ios权限) android/app/src/main/AndroidManifest.xml…

ChatGPT使用小结

ChatGPT是openAI旗下的一款语言模型&#xff0c;说它是语言模型更不如说他是一个全能小助手&#xff0c;自从去年它爆火之后&#xff0c;国内也出现了许多同款语言模型&#xff0c;这些智能语言模型的出现&#xff0c;对于像我这样的低端程序员的效率提升是巨大的。 很多人可能…

(二十七)Flask之数据库连接池DBUtils库

目录&#xff1a; 每篇前言&#xff1a;DBUtils库模式一&#xff08;底层使用threading.local实现&#xff09;&#xff1a;模式二&#xff1a; Flask中使用方式一&#xff1a;直接将DBUtils初始化放到settings.py文件中方式二&#xff1a;从utils文件夹中导入 脚本使用DBUtils…

Qt plugin 开发UI界面插件

目录 1.创建接口 2.创建插件 3.创建插件界面 4.插件实现 5.创建应用工程 6.应用插件 1.创建接口 打开QtCreater&#xff0c;点击左上角“文件”->新建文件或项目&#xff0c;在弹窗中选择C/CHeader File。 输入文件名&#xff0c;选好路径&#xff08;可自行设置名称…

jsonpath在线解析器网址

jsonpath在线解析器网址&#xff1a;https://jsonpath.com/

手动实现简易版RPC(上)

手动实现简易版RPC(上) 前言 什么是RPC&#xff1f;它的原理是什么&#xff1f;它有什么特点&#xff1f;如果让你实现一个RPC框架&#xff0c;你会如何是实现&#xff1f;带着这些问题&#xff0c;开始今天的学习。 本文主要介绍RPC概述以及一些关于RPC的知识&#xff0c;为…

02-结构化程式与自定义函数

视频教程&#xff1a;b站视频【MATLAB教程_台大郭彦甫&#xff08;14课&#xff09;原视频补档】https://www.bilibili.com/video/BV1GJ41137UH/?share_sourcecopy_web&vd_sourc*ed6b9f96888e9c85118cb40c164875dfc 官网教程&#xff1a; MATLAB 快速入门 - MathWorks 中…

【动手学深度学习】15_汉诺塔问题

注&#xff1a; 本系列仅为个人学习笔记&#xff0c;学习内容为《算法小讲堂》&#xff08;视频传送门&#xff09;&#xff0c;通俗易懂适合编程入门小白&#xff0c;需要具备python语言基础&#xff0c;本人小白&#xff0c;如内容有误感谢您的批评指正 汉诺塔&#xff08;To…

基于springboot实现高校学科竞赛平台系统项目【项目源码+论文说明】

基于springboot实现高校学科竞赛平台系统演示 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了高校学科竞赛平台的开发全过程。通过分析高校学科竞赛平台管理的不足&#xff0c;创建了一个计算机管理高校学科竞…

day02 51单片机

51单片机学习 1闪烁LED 1.1 需求描述 这个案例,我们要让P00引脚对应的LED按照1秒闪烁1次。 1.2 硬件设计 1.1 软件设计 1)LED闪烁的代码 想让LED闪烁,就需要P00的值不断在0和1之间循环变化。实现这一功能的代码也很简单: #include <STC89C5xRC.H> //包含STC89…