数据结构六:线性表之顺序栈的设计

目录

一、栈的应用场景

二、栈的基本概念和结构

2.1 栈的基本概念

2.2 栈的结构

2.3 栈的实现方式

三、顺序栈的接口函数实现

3.0  顺序栈的概念和结构

3.1 顺序栈的接口函数 

3.2  顺序栈的设计(结构体)

3.3  顺序栈的初始化

3.4 入栈(相当于顺序表的尾插)

3.5 出栈(相当于顺序表的尾删)

3.6 获取栈顶元素值

3.7 获取有效元素个数

3.8 判空

3.9 判满

3.10 扩容

3.11 打印

3.12 清空

3.13 销毁

四、总结


一、栈的应用场景

       栈是一种非常常见的数据结构,在计算机科学和软件工程中有许多应用场景。以下是一些常见的应用场景:

1. 函数调用和返回:栈被广泛应用于函数调用和返回的过程中。每次函数调用时,当前函数的执行上下文(如局部变量、函数参数、返回地址等)都被压入栈中,在函数返回时再从栈中弹出。

2. 表达式求值:栈可用于中缀表达式到后缀表达式的转换以及后缀表达式的求值过程。在转换过程中,栈用于暂时存储运算符,并根据运算符的优先级进行排序。在求值过程中,栈用于存储操作数,以便进行运算。

3. 递归算法:许多递归算法使用栈来管理递归调用的状态。每次递归调用时,函数的参数和局部变量都被保存在栈中,直到递归结束才被弹出。

4. 浏览器历史记录:浏览器的后退和前进功能可以通过栈来实现。每次访问一个新页面时,该页面的 URL 可以被推入栈中,当用户点击后退按钮时,最近访问的页面 URL 从栈中弹出。

5. 文本编辑器的撤销操作:文本编辑器通常使用栈来实现撤销和重做操作。每次进行编辑操作时,编辑器将操作的内容推入栈中,当用户执行撤销操作时,最近的编辑操作可以从栈中弹出并还原。

6. 括号匹配检查:栈可用于检查括号是否匹配的问题。遍历字符串时,遇到左括号时将其推入栈中,遇到右括号时将栈顶的左括号弹出,最后检查栈是否为空,若为空则说明括号匹配正确。

7. 迷宫求解:在深度优先搜索(DFS)算法中,栈可以用来实现迷宫的求解。每次选择一个方向探索时,将当前位置推入栈中,并在探索结束后回溯时弹出栈顶的位置。

二、栈的基本概念和结构

2.1 栈的基本概念

      它是一种受到限制的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶(top),另一端称为栈底(bottom)。并且它的特点是:栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。没有数据的话叫做空栈。现实生活中的例子:弹夹,盘子的摆放。

基于栈结构的特点,在实际应用中,通常只会对栈执行以下两种操作:

  • 向栈中添加元素,此过程被称为"进栈"(入栈或压栈),入数据在栈顶;
  • 从栈中提取出指定元素,此过程被称为"出栈"(或弹栈),出数据也在栈顶;

2.2 栈的结构

栈是一种只能从表的一端存取数据且遵循 "先进后出" 原则的线性存储结构。 

2.3 栈的实现方式

栈是一种 "特殊" 的线性存储结构,因此栈的具体实现有以下两种方式:

  1. 顺序栈:采用顺序存储结构可以模拟栈存储数据的特点,从而实现栈存储结构;
  2. 链栈:采用链式存储结构实现栈结构;

两种实现方式的区别,仅限于数据元素在实际物理空间上存放的相对位置,顺序栈底层采用的是动态数组(不定长顺序表),链栈底层采用的是单链表。

三、顺序栈的接口函数实现

3.0  顺序栈的概念和结构

        顺序栈,即用顺序表(数组)实现栈存储结构。为保证栈的数据元素的后进先出的高效性,那么我们将栈顶应该设计在数组的哪一端呢?学过顺序表,我们可以知道,顺序表的尾插和尾删的效率非常高,时间复杂度为O(1),因此,参考顺序表,我们可以将栈顶设计为数组的末端,通过指针指向它。(实际中用一个整型变量标记栈顶即可,更加简单方便),因此,我们可以这么通俗的理解顺序栈:限定只能尾插和尾删的顺序表!

3.1 顺序栈的接口函数 

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stack>//系统自带的栈
#include "Stack.h"//初始化
void Init_stack(struct Stack* st);//入栈
void Push(struct Stack* st, ELEM_TYPE val);//出栈
void Pop(struct Stack* st);//获取栈顶元素值
ELEM_TYPE Top(struct Stack* st);//获取有效元素个数
int Get_length(struct Stack* st);//判空
bool IsEmpty(struct Stack* st);//判满
bool IsFull(struct Stack* st);//扩容
void Inc_Stack(struct Stack* st);//打印
void Show(struct Stack* st);//清空
void Clear(struct Stack* st);//销毁
void Destroy(struct Stack* st);

3.2  顺序栈的设计(结构体)

    本质和顺序表的设计差不多,主要为3个成员,栈底指针(相当于顺序表中用来申请内存空间的指针),栈顶指针标记栈顶的位置,简单起见,用整型变量top即可(相当于顺序表中记录有效元素个数的cursize),记录栈的总容量的stacksize(相当于顺序表中的capacity)

typedef struct Stack
{ELEM_TYPE *base;//栈底指针  指向动态数组的指针,用来扩容int top;//栈顶指针,
//标记栈顶位置的指针,此处用整型变量表示栈顶的下标更加方便(当前栈的有效元素个数,也代表下一个元素插入的下标,相当于顺序表中的cursize)int stacksize;//当前总的容量大小}Stack, *PStack;

      stacksize指示栈的最大容量; base为栈底指针,它始终指向栈底位置,若base的值为
NULL,则表明栈结构不存在; top为栈顶指针,其初值指向栈底,即top=base可作为栈空的
标记。每当插入新的栈顶元素时,指针top增加1;删除栈顶元素时,指针top减1.因此,非空
栈中的栈顶指针始终在栈顶元素的下一个位置上。
base == top或者top==0是栈空的重要标志!

3.3  顺序栈的初始化

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

//栈的顺序存储结构体设计
typedef int ELEM_TYPE;
#define INIT_SIZE 10//初始化
void Init_stack(struct Stack* st)
{st->base = (ELEM_TYPE *)malloc(INIT_SIZE * sizeof(ELEM_TYPE));st->top = 0;st->stacksize = INIT_SIZE;
}

3.4 入栈(相当于顺序表的尾插)

      入栈相当于是顺序表的尾插操作,尾插数据的主要思路如下:

第0步:指针参数断言;

第1步:进行判满操作,同时如果顺序栈已满,则要进行扩容,同时要保证扩容成功;

第2步:尾插不需要移动数据,只需要在最后一个元素的下一个格子放入一个元素

第3步:更新顺序栈的有效元素个数变量, st->top++;

 

//入栈
void Push(struct Stack* st, ELEM_TYPE val)
{//0.参数检测st!=NULLassert(st!=NULL);//1.判满,得有空间进行入栈 if(IsFull(st)){Inc_Stack(st); //扩容}//2.往top位置进行入栈st->base[st->top] = val;//3.对应的栈顶指针top需要自增,动一下st->top++;}

3.5 出栈(相当于顺序表的尾删)

    出栈相当于是顺序表的尾删操作,尾删数据的主要思路如下:

第0步:指针参数断言;

第1步:进行判空操作,同时如果顺序栈为空,无法删除,结束程序;

第2步:尾删不需要移动数据;

第3步:更新顺序栈的有效元素个数变量, st->top++。

     只要把最后一个元素数量减掉,最后一个数据去掉,size--,并不影响查找等功能。

​​​​​​​ 

//出栈
void Pop(struct Stack* st)
{//0.参数检测st!=NULLassert(st!=NULL);//1.判空if(IsEmpty(st)){return;}//2.只需要将栈顶指针top向下挪动一位st->top--;
}

3.6 获取栈顶元素值

  栈顶元素对应的下标为top-1,因此直接返回该下标对应的数组元素即可。

//获取栈顶元素值
ELEM_TYPE Top(struct Stack* st)
{//0.参数检测st!=NULLassert(st!=NULL);return st->base[st->top-1];
}

3.7 获取有效元素个数

整型变量top标记的栈顶指针的位置,同时也是有效元素个数,直接返回即可!

//获取有效元素个数
int Get_length(struct Stack* st)
{//0.参数检测st!=NULLassert(st!=NULL);return st->top;
}

3.8 判空

     判空操作很简单,只需要比较base == top或者top==0即可,若相等,则无法进行删除操作!!

//判空
bool IsEmpty(struct Stack* st)
{//0.参数检测st!=NULLassert(st!=NULL);return st->top == 0;
}

3.9 判满

 判满操作很简单,只需要比较top和stacksize是否相等即可,若相等,表明顺序栈已满,此时要进行扩容操作! 

//判满
bool IsFull(struct Stack* st)
{//0.参数检测st!=NULLassert(st!=NULL);return st->stacksize == st->top;
}

3.10 扩容

   当我们插入数据,若空间不够,将要扩容操作:使用realloc函数。

//扩容
void Inc_Stack(struct Stack* st)
{//0.参数检测st!=NULLassert(st!=NULL);st->base = (ELEM_TYPE *)realloc(st->base, st->stacksize*sizeof(ELEM_TYPE) * 2);st->stacksize *= 2;
}

3.11 打印

     打印数组元素,其实和之前用指针方式访问数组一样,for循环遍历通过下标即可,

st->base[i] 。


//打印
void Show(struct Stack* st)
{//0.参数检测st!=NULLassert(st!=NULL);for(int i=0; i<st->top; i++){printf("%d ", st->base[i]);}printf("\n");
}

3.12 清空

      顺序栈的有效数据元素个数,是通过top进行记录的,即使数组容量有10个,只要将top清0,就代表数组为空。 虽然这十个单元格子是存在内存空间的,但是编程者认为没有有效的数据,这便是清空顺序栈!

//清空
void Clear(struct Stack* st)
{//0.参数检测st!=NULLassert(st!=NULL);st->top = 0;
}

3.13 销毁

  因为顺序栈是动态内存分配的,是在堆区利用realloc函数分配的空间,不销毁会有内存泄漏的风险。因此,使用完毕必须要释放,同时释放完后要将指针置空,防止出现野指针!

//销毁
void Destroy(struct Stack* st)
{//0.参数检测st!=NULLassert(st!=NULL);st->top = 0;free(st->base);st->base=NULL;
}

四、总结

      从以上分析,我们可以知道顺序栈只是顺序表的一种特殊情况,限定了数据元素的插入和删除位置,顺序栈是一种基于数组实现的栈,它具有一些优点:

1. 内存连续性:顺序栈的元素在内存中是连续存储的,这使得访问元素非常高效。由于数组支持随机访问,因此可以直接通过索引访问栈中的任何元素,而不需要下一节讲的链式栈那样遍历链表。

2. 简单高效: 顺序栈的实现比较简单,不需要额外的指针来维护元素之间的关系,只需要一个数组和一个指向栈顶的索引即可。这使得其在空间和时间复杂度上都比较高效。

3. 适用性广泛:顺序栈适用于大多数栈的应用场景,无论是简单的数据处理还是复杂的算法实现,都可以方便地使用顺序栈来完成。

4. 易于实现:由于其基于数组的实现方式,顺序栈的操作相对简单,包括入栈、出栈、查看栈顶元素等操作都很容易实现,不需要复杂的指针操作或链表遍历。

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

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

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

相关文章

Python绘制的好看统计图

代码 sx [Accent, Accent_r, Blues, Blues_r, BrBG, BrBG_r, BuGn, BuGn_r, BuPu, BuPu_r, CMRmap, CMRmap_r, Dark2, Dark2_r, GnBu, GnBu_r, Greens, Greens_r, Greys, Greys_r, OrRd, OrRd_r, Oranges, Oranges_r, PRGn, PRGn_r, Paired, Paired_r, Pastel1, Pastel1_r, P…

phpstudy 搭建 upload-labs 文件上传靶场

phpstudy 搭建靶场&#xff1a;下载安装好phpstudy后&#xff0c;下载靶场源码&#xff1a; upload-labs下载地址&#xff1a; https://github.com/c0ny1/upload-labs 下载完压缩文件&#xff0c;解压文件&#xff0c;解压后的文件夹命名为upload--labs 将解压后到文件夹放…

Hive 表定义主键约束

文章目录 1.建表语句2.主键约束3.主键约束的意义参考文献 1.建表语句 先看一下官方给的完整的见表语句&#xff1a; CREATE [TEMPORARY] [EXTERNAL] TABLE [IF NOT EXISTS] [db_name.]table_name -- (Note: TEMPORARY available in Hive 0.14.0 and later)[(col_name data…

Sortable 拖拽行实现el-table表格顺序号完整例子,vue 实现表格拖拽行顺序号完整例子

npm install sortable<template><vxe-modalref"modalRef"v-model"showModal"title"详情"width"70vw"height"60vh"class"his"transfer><el-table ref"tableRef" :data"tableData&q…

Swift - 可选项(Optional)

文章目录 Swift - 可选项&#xff08;Optional&#xff09;1. 可选项&#xff08;Optional&#xff09;2. 强制解包&#xff08;Forced Unwrapping&#xff09;3. 判断可选项是否包含值4. 可选项绑定&#xff08;Optional Binding&#xff09;5. 等价写法6. while循环中使用可选…

CogAgent:开创性的VLM在GUI理解和自动化任务中的突破

尽管LLMs如ChatGPT在撰写电子邮件等任务上能够提供帮助&#xff0c;它们在理解和与GUIs交互方面存在挑战&#xff0c;这限制了它们在提高自动化水平方面的潜力。数字世界中的自主代理是许多现代人梦寐以求的理想助手。这些代理能够根据用户输入的任务描述自动完成如在线预订票务…

GraspNet-1Billion 论文阅读

文章目录 GraspNet-1Billion总体数据集评价指标网络pointnet&#xff1a;Approach Network:Operation Network&#xff1a;Tolerance Network 摘要相关工作基于深度学习的抓取预测算法抓取数据集点云深度学习 GraspNet-1Billion CVPR2020 上海交大 论文和数据集地址&#xff1…

翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深度学习五

合集 ChatGPT 通过图形化的方式来理解 Transformer 架构 翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深度学习一翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深度学习二翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深…

Linux下启动jenkins报错问题解决

jenkins端口报错 java.io.IOException: Failed to start Jettyat winstone.Launcher.<init>(Launcher.java:209)at winstone.Launcher.main(Launcher.java:496)at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at java.base/jdk.int…

《QT实用小工具·四十八》趣味开关

1、概述 源码放在文章末尾 该项目实现了各种样式的趣味开关&#xff1a; 1、爱心形状的switch开关&#xff0c;支持手势拖动、按压效果 2、线条样式的3种开关 项目demo演示如下所示&#xff1a; 使用方式&#xff1a; 1、sapid_switch文件夹加入工程&#xff0c;.pro文件中…

Go 语言(三)【面向对象编程】

1、OOP 首先&#xff0c;Go 语言并不是面向对象的语言&#xff0c;只是可以通过一些方法来模拟面向对象。 1.1、封装 Go 语言是通过结构体&#xff08;struct&#xff09;来实现封装的。 1.2、继承 继承主要由下面这三种方式实现&#xff1a; 1.2.1、嵌套匿名字段 //Add…

InfluxDB安装使用介绍

1.介绍 InfluxDB是一个由InfluxData开发的开源时序型数据。它由Go写成&#xff0c;着力于高性能地查询与存储时序型数据。InfluxDB被广泛应用于存储系统的监控数据&#xff0c;IoT行业的实时数据等场景。 2.对常见关系型数据库&#xff08;MySQL&#xff09;的基础概念对比 1…

ubuntu22.04 修改内核源码教程

1. 确认当前内核版本 uname -a 2. 去ubuntu官网下载对应版本内核源码 6.5.0-28.29 : linux package : Ubuntu (launchpad.net) 3. 准备编译环境 sudo apt-get install libncurses5-dev libssl-dev build-essential openssl flex bison libelf-dev tar -xzvf linux_6.5.…

8_手眼标定总结_auboi5机械臂与海康平面相机

经过不断地学习与调试&#xff0c;不断地学习网络上其他同志分享的资料&#xff0c;opencv手眼标定迎来了阶段性结束。实际测试结果在机械臂坐标系中X方向差5mm左右。 代码参考《https://blog.csdn.net/wanggao_1990/article/details/81435660》 注意事项&#xff1a; ①标定…

nacos-redis-springboot

新项目 准备工作 nacos 版本 2.0.3 redis 最终版本说明 springcloud-alibaba&#xff1a;2.2.7RELEASE springcloud&#xff1a;Hoxton.SR12 springboot&#xff1a;2.3.12.RELEASE Nacos&#xff1a;2.0.3 步骤 启动nacos和redis 准备nacos配置文件 server: port…

Node私库Verdaccio使用记录,包的构建,推送和拉取

Node私库Verdaccio使用记录&#xff0c;包的构建&#xff0c;推送和拉取 Verdaccio是一个轻量级的私有npm代理注册中心&#xff0c;它可以帮助你在本地搭建一个npm仓库&#xff0c;非常适合企业内部使用。通过使用Verdaccio&#xff0c;你可以控制和缓存依赖包&#xff0c;提高…

基于Pytorch深度学习——多层感知机

本文章来源于对李沐动手深度学习代码以及原理的理解&#xff0c;并且由于李沐老师的代码能力很强&#xff0c;以及视频中讲解代码的部分较少&#xff0c;所以这里将代码进行尽量逐行详细解释 并且由于pytorch的语法有些小伙伴可能并不熟悉&#xff0c;所以我们会采用逐行解释小…

Upload-labs 靶场通关解析(上)

前言 文件上传漏洞是一种常见的网络安全漏洞&#xff0c;存在于许多Web应用程序中。攻击者利用这个漏洞可以上传恶意文件到目标服务器&#xff0c;从而执行各种恶意操作&#xff0c;如执行恶意代码、获取敏感信息、控制服务器等。 文件上传漏洞的原理是&#xff0c;Web应用程…

【精选文献】JAG|基于时序Sentinel-1 SAR影像小农耕作区烟草空间分布制图

目录 文章简介 01 文章摘要 02 研究背景、目标及创新点 03 研究区域与数据集 04 研究方法 05 研究结果 06 研究讨论 07 研究结论 08 文章引用 文章简介 论文名称&#xff1a;Mapping tobacco planting areas in smallholder farmlands using Phenological-Spatial-Te…

jenkins汉化不完全问题解决

jenkins安装完Localization:Chinese(Simplified)中文语言包后&#xff0c;发现是出现汉化不完全或者部分汉化的情况&#xff0c;如下图&#xff1a; 解决方法&#xff1a; 启动命令中指定语言 -Duser.languageen_US.UTF-8 或者 -Duser.languageC.UTF-8原因分析&#xff1a;安…