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

news2024/11/24 5:17:19

目录

一、栈的应用场景

二、栈的基本概念和结构

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!=NULL
    assert(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!=NULL
    assert(st!=NULL);

	//1.判空
	if(IsEmpty(st))
	{
		return;
	}

	//2.只需要将栈顶指针top向下挪动一位

	st->top--;
}

3.6 获取栈顶元素值

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

//获取栈顶元素值
ELEM_TYPE Top(struct Stack* st)
{
   //0.参数检测st!=NULL
    assert(st!=NULL);

	return st->base[st->top-1];
}

3.7 获取有效元素个数

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

//获取有效元素个数
int Get_length(struct Stack* st)
{
   //0.参数检测st!=NULL
    assert(st!=NULL);

	return st->top;
}

3.8 判空

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

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

3.9 判满

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

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

3.10 扩容

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

//扩容
void Inc_Stack(struct Stack* st)
{
    //0.参数检测st!=NULL
    assert(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!=NULL
    assert(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!=NULL
    assert(st!=NULL);

	st->top = 0;
}

3.13 销毁

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

//销毁
void Destroy(struct Stack* st)
{
    //0.参数检测st!=NULL
    assert(st!=NULL);

	st->top = 0;
	free(st->base);
    st->base=NULL;
}

四、总结

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

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

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

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

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

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

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

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

相关文章

Python转换文本文件为PDF文档,绘制文本到PDF文档页面

文本文件因其轻便、易编辑的优势&#xff0c;常用于日常文字记录与数据交换&#xff1b;而PDF文档则以高保真、格式稳定和良好的阅读体验&#xff0c;成为正式报告、文献发布等场景的首选。将文本文件转为PDF&#xff0c;在PDF内精准绘制文本&#xff0c;旨在兼顾内容的规范呈现…

云计算革新:以太网 Scale-UP 网络为 GPU 加速赋能

谈谈基于以太网的GPU Scale-UP网络 Intel Gaudi-3 采用 RoCE 互联技术&#xff0c;促进了 Scale-UP 解决方案。业界专家 Jim Keller 倡导以太网替代 NVLink。Tenstorrent 成功应用以太网实现片上网络互联。RoCE 和以太网已成为互联解决方案的新兴趋势&#xff0c;为高性能计算提…

区块链技术--编译BSV源码(v1.0.1)

编译好的可执行文件bitcoin-cli 和 bitcoind 下载: https://github.com/youngqqcn/QBlockChainNotes/blob/master/BTC%E7%B3%BB%E5%B1%B1%E5%AF%A8%E5%B8%81/my_bsv_v1.0.1.tar.gz 安装 libminiupnpc wget https://download-ib01.fedoraproject.org/pub/epel/7/x86_64/Packa…

商店数据(六)

目录 41.短信发送记录表 42.职员登录记录表 43.会员登录记录表 ​44.后台菜单表 45.商城信息表 46.消息队列表 47.移动端首页按钮管理表 48.商城导航表 41.短信发送记录表 CREATE TABLE wst_log_sms (smsId int(11) NOT NULL AUTO_INCREMENT COMMENT 自增ID,smsSrc t…

IOCP实现UDP Server

IOCP实现UDP Server 1、IOCP原理图 参考文献1&#xff1a;IOCP详解-阿里云开发者社区 (aliyun.com) 参考文献2&#xff1a;IOCP编程之基本原理 - 史D芬周 - 博客园 (cnblogs.com) 原理图 同步以及异步 2、UDP Server代码以及测试代码 // iocpudpdemo.cpp : 此文件包含 &qu…

再谈钓鱼邮件

再谈钓鱼邮件 概述 最近对邮件的防御策略进行了更新&#xff0c;结合威胁情报和安全沙箱对收到的钓鱼邮件进行了分析&#xff0c;期望这些案例能对大家有所帮助。 网关上拦截的钓鱼邮件基本可以分三个类别&#xff1a;链接钓鱼邮件、附件钓鱼邮件以及邮件头伪造钓鱼邮件&…

在谷歌浏览器访问特定的网站 提示此网站无法提供安全连接

1、问题描述&#xff1a; 最近通过谷歌浏览器访问某些网址提示此网站无法提供安全连接&#xff0c;换一个浏览器就能正确打开&#xff01; 例子如下&#xff1a; 访问 https://baijiahao.baidu.com/s?id1788533041823242656 2、查找原因 通过控制台发现请求未有响应码&#xf…

【数据分析面试】34.填充NaN值 (Python:groupby/sort_value/ffill)

题目&#xff1a;填充NaN值 &#xff08;Python) 给定一个包含三列的DataFrame&#xff1a;client_id、ranking、value 编写一个函数&#xff0c;将value列中的NaN值用相同client_id的前一个非NaN值填充&#xff0c;按升序排列。 如果不存在前一个client_id&#xff0c;则返…

小红书从记忆机制解读信息检索,提出新范式获得 EACL Oral

近日&#xff0c;来自小红书搜索算法团队的论文《Generative Dense Retrieval: Memory Can Be a Burden》被自然语言处理领域国际会议 EACL 2024 接收为 Oral&#xff0c;接受率为 11.32%&#xff08;144/1271&#xff09;。 他们在论文中提出了一种新颖的信息检索范式——生成…

python环境安装jupyter

安装完毕之后下一步可以参考&#xff1a;配置jupyter的启动路径-CSDN博客 1 前提条件&#xff1a;python环境 系统&#xff1a;win10 python&#xff1a;本地已经有python&#xff0c;可以查看本地的python版本&#xff1a; C:\Users\PC>python --version Python 3.8.10 …

为什么本国货币的贬值会导致本国物价的上涨

本国货币贬值意味着其对外国货币的汇率下降&#xff0c;也就是说&#xff0c;同样数量的本国货币现在能兑换的外国货币减少了。这种情况下&#xff0c;本国货币的购买力相对于外国货币减弱了。本国货币贬值可能导致本国物价上涨的几个原因&#xff1a; 进口成本上升&#xff1a…

38.WEB渗透测试-信息收集-信息收集-企业信息收集(5)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;37.WEB渗透测试-信息收集-企业信息收集&#xff08;4&#xff09; 上个内容用到了cdn&am…

Windows Server 评估版转换(升级)为完整版

临时方法 获取 Windows Server 的剩余宽限期 Slmgr /dliWindows Server免费试用期可以使用以下命令合法延长5次&#xff0c;共180天&#xff1a; slmgr /rearm这意味着所评估的 Windows Server 的最长可用时间为 3 年 ( 180 days * 6)。 试用期到期后&#xff0c;Windows S…

Python对Excel两列数据进行运算

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 Python对Excel两列数据进行运算 在日常工作中&#xff0c;经常会遇到需要对Excel表格中的数…

win中python中OpenCV使用cv2.imshow()报错的解决办法

1. 问题 cv2.error: OpenCV(4.9.0) D:\a\opencv-python\opencv-python\opencv\modules\highgui\src\window.cpp:1272: error: (-2:Unspecified error) The function is not implemented. Rebuild the library with Windows, GTK 2.x or Cocoa support. If you are on Ubuntu o…

STM32读写备份寄存器BKP

今天学习的读写STM32的备份寄存器BKP的步骤&#xff0c;这节知识是比较简单的&#xff0c;一共也就两大部&#xff1a; 这个BKP寄存器的意思就是在芯片的VB引脚上接个电池&#xff0c;就能保存其寄存器中的数据掉电不丢失。先来看看电池的接法&#xff1a; 好&#xff0c;下面…

3分钟了解拍摄VR全景需要哪些硬件

VR全景图片是一张水平方向360度&#xff0c;垂直方向180度&#xff0c; 图片尺寸宽高比为2:1的图片。 通过720yun APP或720yun官网上传生成全景H5页面&#xff0c;即可360度全方位观看画面中的景象。 拍摄VR全景有很多方法&#xff0c;下面介绍用单反相机、全景相机、智能手机…

【Paddle】PCA线性代数基础 + 领域应用:人脸识别算法(1.1w字超详细:附公式、代码)

【Paddle】PCA线性代数基础及领域应用 写在最前面一、PCA线性代数基础1. PCA的算法原理2. PCA的线性代数基础2.1 标准差 Standard Deviation2.2 方差 Variance2.3 协方差 Covariance2.4 协方差矩阵 The Covariance Matrix2.5 paddle代码demo①&#xff1a;计算协方差矩阵2.6 特…

华院计算登榜『2024福布斯中国人工智能科技企业TOP 50』

4月28日&#xff0c;福布斯中国正式发布“2024福布斯中国人工智能科技企业TOP 50”榜单。华院计算凭借其在人工智能领域的卓越成就与深远影响力&#xff0c;荣膺殊荣&#xff0c;成功跻身榜单。 工业和信息化部2024年4月表示&#xff0c;中国人工智能企业数量已超过4,500家。AI…

你的3D模型不是数字孪生!

大型资产的新技术往往是根据其带来运营效率提升的能力来判断的。 但是&#xff0c;当一项令人兴奋的创新出现时&#xff0c;运营商通常会承担经过计算的风险并对其潜力进行投资&#xff0c;即使该技术缺乏保证在规定时间范围内带来收益的验证。 数字孪生&#xff08;digital …