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.hqwc.cn/news/596712.html

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

相关文章

计算机组成与体系结构

数据的表示 进制的转换 二进制转十进制 10100.01 1 ∗ 2 4 1 ∗ 2 2 1 ∗ 2 − 2 10100.011 *2^41*2^21*2^{-2} 10100.011∗241∗221∗2−2 八进制转十进制 753 7 ∗ 8 2 5 ∗ 8 1 3 ∗ 8 1 491 753 7 * 8^25*8^13*8^1 491 7537∗825∗813∗81491 $$ $$ 十六进制…

4.1.k8s的pod-创建,数据持久化,网络暴露,env环境变量

目录 一、Pod介绍 二、指令创建和管理Pod 三、资源清单创建pod 1.挂载hostPath存储卷 2.NFS存储卷 所有节点安装nfs k8s3编辑NFS配置文件 k8s1&#xff0c;k8s2节点开机挂载 编辑pod资源清单&#xff0c;挂载nfs 四、pod网络暴露 1.hostNetwork使用宿主机的网络 2.…

【OneAPI】贴纸生成API

OneAPI新接口发布&#xff1a;贴纸生成 生成一个10241024像素的贴纸。 API地址&#xff1a;POST https://oneapi.coderbox.cn/openapi/api/stickers 请求参数&#xff08;body&#xff09; 参数名类型必填含义说明prompt提示词是提示词示例&#xff1a;一只可爱的小狗 响应…

ZYNQ学习之Petalinux 设计流程实战

基本都是摘抄正点原子的文章&#xff1a;<领航者 ZYNQ 之嵌入式Linux 开发指南 V3.2.pdf&#xff0c;因初次学习&#xff0c;仅作学习摘录之用&#xff0c;有不懂之处后续会继续更新~ PetaLinux工具提供了在 Xilinx 处理系统上自定义、构建和部署嵌入式 Linux 解决方案所需的…

【ARM 嵌入式 C 常用数据结构系列 25.1 -- linux 双向链表 list_head 使用详细介绍】

请阅读【嵌入式开发学习必备专栏 】 文章目录 内核双向链表双向链表的数据结构初始化双向链表在双向链表中添加元素遍历双向链表链表使用示例注意事项 内核双向链表 在Linux内核中&#xff0c;双向链表是一种广泛使用的数据结构&#xff0c;允许从任意节点高效地进行前向或后向…

FPGA笔试面试题目记录

1 logic utilization 题目&#xff1a;Rank the following operations from lowest utilization to highest. Assume that all variables are 32-bit integers,that the operations are implemented using LUTs ony and that the synthesiser will produce an optimal digital…

1.微服务

一、微服务是什么 微服务是一种架构风格&#xff0c;即&#xff0c;一个应用应该是一组小型服务&#xff0c;每个服务器只负责一种服务&#xff0c;服务之间可以通过 HTTP 的方式进行互通。每一个功能元素最终都是一个可独立替换和独立升级的软件单元。 可以说&#xff0c;微…

双连通分量算法

1. 连通图概念 连通图&#xff1a;无向图任意两点之间存在通路。 强连通&#xff1a;有向图&#xff08;前提&#xff09;中&#xff0c;任意两点都有至少一条通路&#xff0c;则此图为强连通图。 弱连通图&#xff1a;将有向图的有向边换成无向边得到的图是连通图&#xff0c…

Spring Cloud介绍

一、SpringCloud总体概述 Cloud Foundry Service Broker&#xff1a;通用service集成进入Cloud Foundry Cluster&#xff1a;服务集群 Consul&#xff1a;注册中心 Security&#xff1a;安全认证 Stream&#xff1a;消息队列 Stream App Starters&#xff1a;Spring Cloud Stre…

面试题——JVM老年代空间担保机制(我的想法)

这里借用一下人家的图&#xff0c;来说一下我的想法&#xff0c;嘻嘻。。。。 原文链接&#xff1a;一道面试题&#xff1a;JVM老年代空间担保机制-CSDN博客? 嗯&#xff0c;我觉得老年代担保机制的主要作用就是避免频繁触发FULL GC&#xff0c;这其实也是因为年轻代Minor GC…

自动驾驶_交通标志识别:各目标检测算法评测

自动驾驶|交通标志识别&#xff1a;各目标检测算法评测 论文题目&#xff1a;Evaluation of Deep Neural Networks for traffic sign detection systems 开源代码&#xff1a;https://github.com/aarcosg/traffic-sign-detection 附赠自动驾驶学习资料和量产经验&#xff1a;…

sharo反序列化漏洞

启动docker 服务 sudo service docker start 打开靶场 sudo docker run -d -p 80:8080 medicean/vulapps:s_shiro_1 输入docker虚拟机地址打开靶机sharo框架 如何利用漏洞 打开工具目录在终端中打开 输入靶机地址 打开yaki监听端口可以设置为6666 返回工具填写靶机ip和端口 …