数据结构资料汇编:栈

news2024/12/23 23:35:56

数据结构资料汇编:栈

定义

栈(stack)是限定仅在表尾进行插入或删除操作的线性表。

  • 表尾称为栈顶(top),可以进行插入或删除操作

    • 栈的插入操作称为 进栈入栈(push)
    • 栈的删除操作成为 出栈退栈(pop)
  • 表头称为栈底(bottom)

  • 不含元素的空表称为空栈

  • 操作特性:后进先出(Last In First Out,LIFO)

抽象数据类型

ADT Stack {
    数据对象: D = {a_i | a_i in ElemSet, i=1,2,...,n, n<=0}
    数据关系: R = {<a_{i-1}, a_i> | a_{i-1}, a_i in D, i=2,...,n},约定 a_n 端为栈顶,a_1 端为栈底。
    基本操作:
        InitStack(&S)
            操作结果:构造一个空栈 S
        DestroyStack(&S)
            初始条件:栈 S 已存在
            操作结果:栈 S 被销毁
        ClearStack(&S)
            初始条件:栈 S 已存在
            操作结果:将栈 S 清为空栈
        StackEmpty(S)
            初始条件:栈 S 已存在
            操作结果:若栈 S 为空栈,则返回 true,否则返回 false
        StackLength(S)
            初始条件:栈 S 已存在
            操作结果:返回 S 的元素个数,即栈的长度。
        GetTop(S)
            初始条件:栈 S 已存在且非空
            操作结果:返回 S 的栈顶元素,不修改栈顶指针。
        Push(&S, e)
            初始条件:栈 S 已存在
            操作结果:插入元素 e 为新的栈顶元素。
        Pop(&S, &e)
            初始条件:栈 S 已存在且非空。
            操作结果:删除 S 的栈顶元素,并用 e 返回其值。
        StackTraverse(S)
            初始条件:栈 S 已存在且非空。
            操作结果:从栈底到栈顶依次对 S 的每个数据元素进行访问
}ADT Stack

栈的存储结构:

  • 顺序栈:利用顺序存储结构实现的栈,即利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,同时附设指针 top 指示栈顶元素在顺序表中的位置。
  • 链栈:指采用链式存储结构实现的栈。通常用单链表表示。

顺序栈的存储结构

  1. 参考资料 [1] 中关于顺序栈的定义:
define MAXSIZE 100 //顺序栈存储空间的初始分配量
typedef struct {
    SElemType *base;  //栈底指针
    SElemType *top;   //栈顶指针
    int stacksize;    //栈可用的最大容量
}SqStack;
  • base 为栈底指针,初始化完成后,栈底指针 base 始终指向栈底的位置(栈底元素的下边沿),若 base 的值为 NULL ,则表明栈结构不存在。
  • top 为栈顶指针,其初始值指向栈底。每当掺入新的栈顶元素时,指针 top 增加 1;删除栈顶元素时,指针 top 减 1。因此,栈空时, topbase 的值相等,都指向栈底;栈非空时, top 始终指向栈顶。
  • stacksize 指示栈可使用的最大容量。
  • 注意区分,参考资料 [1] 中以下表述:
    • 栈顶元素:线性表的最后一个的元素;
    • 栈底元素:线性表的第一个元素;
    • 栈顶:线性表的表尾,即最后一个元素的“后边”。
    • 栈底:线性表的表头,即第一个元素的“前边”。
alt
  1. 参考资料 [2] 中关于顺序栈的定义:
define MaxSize 100 //顺序栈存储空间的初始分配量
typedef struct
{

    ElemType data[MaxSize]; //存放栈中数据元素
    int top;  //栈顶指针,即存放栈顶元素在 data 数组中的下标
}SqStack;

设置顺序栈 s 的栈顶指针初始值 s->top = -1 ,则:

  • 栈空的条件: s->top == -1
  • 栈满的条件: s->top == MaxSize - 1 (即 data 数组的最大下标)
  • 元素 e 进栈:先将栈顶指针 top 增 1,然后将元素 e 放在栈顶指针处
  • 出栈操作:先将栈顶指针处的元素取出放在 e 中,然后将栈顶指针减 1
alt

说明:

以上两种定义,本质上是一样的。在第一种定义中,栈 s 的指针 s.base 在对栈进行操作时不变化,因此可以用它指向栈的数据元素。

另外,两种定义内的 s.top 起始值不同,在插入和删除的时候,会有所差异。

顺序栈的基本操作

顺序栈的初始化

使用参考资料 [1] 中的顺序栈数据结构:

//参考资料 [1] 的算法3.1
Status InitStack(SqStack &S){//构造一个空栈 S
    S.base = new SElemType[MAXSIZE];//为顺序栈动态分配一个最大容量为 MAXSIZE 的数组空间
    //S.base=(SElemType *)malloc(MAXSIZE*sizeof(SElemType));
    if(!S.base) exit(OVERFLOW);//存储分配失败
    S.top = S.base; //top 初始值为 base,空栈
    S.stacksize = MAXSIZE;//stacksize 设置为栈的最大容量 MAXSIZE
    return OK;
}

使用参考资料 [2] 中的顺序栈数据结构:

void InitStack(SqStack * &s){
    s = (SqStack *)malloc(sizeof(SqStack));  //分配一个顺序栈空间,首地址存放在 s 中
    s->top = -1;   //栈顶指针置为 -1
}

顺序栈的入栈

//参考资料 [1] 的算法3.2
Status Push(SqStack &S, SElemType e){//插入元素 e 为新的栈顶元素
    if(S.top - S.base == S.stacksize) retuan ERROR; //栈满
    ((*S).top)++ =  e;//元素 e 压入栈顶,栈顶指针加 1。注意顺序,先赋值,再加 1
    return OK;
}

注意此处入栈的操作顺序,先完成 (*S).top = e 赋值,再执行 (*S).top++ 指针加 1。这是由参考资料 [1] 所确定的栈的数据结构决定的,即初始化时:S.top = S.base ,且 S.base 指向数组的第一个,即索引为 0 。

如果使用参考资料 [2] 的数据结构,即初始化时:s.top = -1 (该书中写作 s->top = -1),则入栈的操作是(参考资料 [2] 的 81 页):

bool Push(SqStack * &s, ElemType e){
    if (s->top == MaxSize-1return false;  //栈满的情况,即栈上溢出
    s->top++;   //栈顶指针增 1
    s->data[s->top] = e;   //元素 e 放在栈顶指针处
    return true;
}

顺序栈的出栈

//参考资料 [1] 的算法3.3
Status Pop(SqStack &S, SElemType &e){//删除 S 栈顶元素,用 e 返回其值
    if (S.top == S.base) return ERROR;  //栈空
    e = *--S.top;  //栈顶指针减1, 将栈顶元素赋值给 e
    return OK;
}
//参考资料 [2] 的算法
bool Pop(SqStack * &s, ElemType &e){
    if (s->top == -1return false;  //栈为空的情况,即栈下溢出
    e = s->data[s->top];   //取栈顶元素
    s->top--;    //栈顶指针减 1
    return true;
}

销毁顺序栈

//针对 [1] 的栈结构,来自 [3]
void DestroyStack(SqStack *S)
/* 销毁栈S,S不再存在 */
 free((*S).base);
 (*S).base=NULL;
 (*S).top=NULL;
 (*S).stacksize=0;
}
//针对 [2] 的栈结构
void DestroyStack(SqStack * s){
    free(s);
}

判断顺序栈是否为空

//针对 [1] 的栈结构,来自 [3]
Status StackEmpty(SqStack S)
/* 若栈S为空栈,则返回TRUE,否则返回FALSE */
 if(S.top == S.base)
  return TRUE;
 else
  return FALSE;
}
//针对 [2] 的栈结构
bool StackEmpty(SqStack * s){
    return (s->top == -1);
}

读取顺序栈的栈顶元素

//针对 [1] 的栈结构,来自 [3]
Status GetTop(SqStack S,SElemType *e)
/* 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR */
 if(S.top > S.base)
 {
  *e=*(S.top-1);
  return OK;
 }
 else
  return ERROR;
}
//针对 [2] 的栈结构
bool GetTop(SqStack * s, ElemType &e){
    if (s->top = -1return false;  //栈为空,即栈下溢出
    e = s->data[s->top];   //取栈顶元素
    return true
}

清空顺序栈

//针对 [1] 的栈结构,来自 [3]
void ClearStack(SqStack *S)
/* 把S置为空栈 */
 (*S).top=(*S).base;
}

顺序栈的长度

//针对 [1] 的栈结构,来自 [3]
int StackLength(SqStack S)
/* 返回S的元素个数,即栈的长度 */
 return S.top-S.base;
}

依次访问顺序栈所有元素

//针对 [1] 的栈结构,来自 [3]
void StackTraverse(SqStack S, void(*visit)(SElemType))
/* 从栈底到栈顶依次对栈中每个元素调用函数visit() */
 while(S.top > S.base)
  visit(*S.base++);
 printf("\n");
}

链栈的存储结构

  1. 参考资料 [1] 中关于链栈的定义:

    //链栈的存储结构
    typedef struct StackNode{
        ElemType data;
        struct StackNode *next;
    }StackNode, *LinkStack;
    • 以链表的头部作为栈顶

    • 不附加头结点

      alt
  2. 参考资料 [2] 中关于链栈的定义:

    typedef struct linknode{
        ElemType data;   //数据域
        struct linknode * next;   //指针域
    }LinkStNode;
    • 采用带头结点的单链表实现链栈。

      alt
    • 链栈的优点:不存在栈满上溢出情况。

    • 规定所有操作都在单链表的表头进行。首结点是栈顶结点,尾结点是栈底结点。

    • 栈空的条件:s->next == NULL

    • 栈满的条件:由于只有内存溢出时才出现栈满,通过长不考虑此情况,所以在链栈中可以看成不存在栈满

    • 元素 e 进栈操作:新建一个结点存放元素 e (由 p 指向它),将结点 p 插入到头结点之后

    • 出栈操作:取出首结点的 data 值并将其删除。

链栈的基本操作

链栈的初始化

//参考资料 [1] 的算法3.5
Status InitStack(LinkStack &S){//构造一个空栈 S,栈顶指针为置空
    S = NULL;
    return OK;
}
//参考资料 [2]
void InitStack(LinkStNode * &s){
    s = (LinkStNode *)malloc(sizeof(LinkStNode));
    s->next = NULL;
}

时间复杂度:

链栈的入栈

//参考资料 [1] 的算法3.6
//此处的单链表不带头结点
Status Push(LinkStack &S, SElemType e){//在栈顶插入元素 e
    p = new StackNode;  //生成新结点
    p->data = e;   // 将新结点数据域置为 e
    p->next = S;  //将新结点插入栈顶
    S = p;  //修改栈顶指针为 p
    return OK;
}
//参考资料 [2]
//注意,在这里用带头结点的单链表
vodi Push(LinkStNode * &s, ElemType e){
    LinkStNode *p;
    p = (LinkStNode *)malloc(sizeof(LinkStNode));  //新建节点 p
    p->data = e;  //存放元素 e
    p->next = s->next; //将 p 结点插入作为首元结点
    s->next = p; 
}

时间复杂度:

链栈的出栈

//参考资料 [1] 的算法3.7
Status Pop(LinkStack &S, SElemType &e){//删除 S 的栈顶元素,用 e 返回其值
    if (S == NULLreturn ERROR;  //栈空
    e = S->data;  //将栈顶元素赋给 e
    p = S;   //用 p 临时保存栈顶元素空间,以备释放
    S = S->next;    //修改栈顶指针
    delete p;    //释放原栈顶元素空间
    return OK;
}
//参考资料 [2]
bool Pop(LinkStNode * &s, ElemType &e){
    LinkStNode * p;
    if(s->next == NULLreturn false;  //栈空的情况
    p = s->next;  // p 指向首元结点
    e = p->data;  //提取首元结点的值
    s->next = p->next;  //删除首元结点
    free(p);   //释放被删除的结点存储空间
    return true;
}

时间复杂度:

取链栈的栈顶元素

//参考资料 [1] 的算法3.8
SElemType GetTop(LinkStack S){//返回 S 的栈顶元素,不修改栈顶指针
    if (S != NULL){//栈非空
        return S->data;  //返回栈顶元素的值,栈顶指针不变
    }
}
//参考资料 [2]
bool GetTop(LinkStNode * s, ElemType &e){
    if(s->next != NULLreturn false;  //栈空
    e = s->next->data;
    return true;
}

时间复杂度:

销毁链栈

//参考资料 [2]
void DestroyStack(LinkStNode * &s){
    LinkStNode *pre = s, *p = s->next; //pre指向头结点,p指向首元结点
    while(p != NULL){
        free(pre); //释放 pre 结点
        pre = p;
        p = pre->next;  //pre,p同步后移
    }
    free(pre);  //此时 pre 指向尾结点,释放其空间
}

时间复杂度:

判断链栈是否为空

//参考资料 [2]
bool StackEmpty(LinkStNode * s){
    return (s->next == NULL);
}

与判断单链表是否为空的方法一样。

//参考资料 [3] ,不带头结点的单链表
Status ListEmpty(LinkList L)
/* 初始条件:线性表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE */
 if(L)
  return FALSE;
 else
  return TRUE;
}

参考资料

[1]. 严蔚敏等. 数据结构:C语言版[M]. 北京:人民邮电出版社,2015.

[2]. 李春葆. 数据结构教程. 北京:清华大学出版社,2017.

[3]. 维基百科:栈[EB/OL]. https://zh.wikipedia.org/wiki/%E5%A0%86%E6%A0%88 , 2023.2.2

本文由 mdnice 多平台发布

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

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

相关文章

Google Play 上的 Shady Reward 应用累积了 2000 万次下载

一种新的活动跟踪应用程序类别最近在 Android 的官方应用程序商店 Google Play 上取得了巨大成功&#xff0c;已在超过 2000 万台设备上下载。 这些应用程序将自己宣传为健康、计步器和养成良好习惯的应用程序&#xff0c;承诺为用户在日常生活中保持活跃、达到距离目标等提供…

字节青训前端笔记 | 跨端技术概述

本节课程内容会分为以下几个方面&#xff1a; 跨端是什么&#xff0c;给大家介绍跨端产生的背景及解决的问题跨端技术方案介绍&#xff0c;给大家介绍目前主流的跨端技术方案&#xff08;hybrid 方案/原生渲染方案/自渲染方案/小程序方案&#xff09;以及对比基于小程序跨端实…

苹果或将打造 “空气键盘”

苹果MR头显玩法大揭秘前言苹果MR头显要来了打造 “空气键盘”眼动追踪与手部追踪一键切换VR/AR模式前言 随着2021年10月FaceBook正式改名Meta后&#xff0c;标志着元宇宙元年的正式到来&#xff0c;元宇宙行业开始出现井喷式的爆发。再到2022年10月&#xff0c;“飞天云动”在…

欧科云链链上卫士:2023年1月安全事件盘点

一、基本信息 2023年1月安全事件共造成约1438万美元损失&#xff0c;相比上个月的安全事件损失金额大幅度下降。其中多链项目LendHub 被攻击&#xff0c;损失高达600万美元&#xff0c;为本月资金损失最大的安全事件。本月RugPull数量基本与上月持平。社媒诈骗等事件依然频发&a…

分布式微服务3

目录 Feign远程调用 基于Feign远程调用 Feign替代RestTemplate 1.引入依赖 2.添加注释 3.编写Feign的客户端 4.测试 5.总结 自定义配置 1.引入依赖 2.配置连接池 3.总结 Gateway网关 Gateway快速入门 1.创建gateway服务&#xff0c;引入依赖 2.编写启动类 3.编写…

【微服务】Nacos集群搭建

Nacos集群搭建1.集群结构图2.搭建集群2.1.初始化数据库2.2.下载nacos2.3.配置Nacos2.4.启动2.5.nginx反向代理2.6.优化1.集群结构图 官方给出的Nacos集群图&#xff1a; 其中包含3个nacos节点&#xff0c;然后一个负载均衡器代理3个Nacos。这里负载均衡器可以使用nginx。 我们…

实战:进入Linux系统 紧急模式,重置root密码

实战:进入CentOS 7紧急模式,重置root密码 实战场景:公司的一台CentOS 7系统忘记root密码了,需要快速把root密码修改为 qwer,找回root身份。 (1)重启系统,在出现内核选择界面时(在此界面中,如果不按键盘的上下方向键,则在默认时间过去后,自动选择光标所选的内核并…

题库——“计算机基础”

小雅兰为开学考试而奋斗 模块一 计算机基础概述 &#xff08;1&#xff09;信息技术基本知识 &#xff08;2&#xff09;计算机的发展与应用 &#xff08;3&#xff09;计算机的工作原理 &#xff08;4&#xff09;计算机系统的组成 &#xff08;5&#xff09;微型计算机主要硬…

九龙证券|AIGC彻底火了,概念股全线上涨,走势领先者三连板!

一夜之间&#xff0c;AIGC走红A股&#xff0c;谁会成为商场宠儿&#xff1f; A股历来盛行炒作新体裁&#xff0c;尤其是新体裁诞生的初期&#xff0c;资金参与热心高涨&#xff0c;诞生牛股概率更高。诞生不足两月的ChatGPT便是今年以来最抢手体裁之一&#xff0c;由ChatGPT带动…

Bug:浏览器一直访问旧资源

Bug&#xff1a;浏览器一直访问旧资源 1 问题阐述 今天在查看自己部署的项目时候&#xff0c;发现浏览器总是访问旧的资源 2 排查思路 2.1 浏览器缓存问题 清除浏览器缓存 ctrl shift del2.2 tomcat问题 我的项目是部署在tomcat上的&#xff0c;因此我怀疑是tomcat的配置问…

C语言学习-ProtoThread

一、简介随着RTOS的应用&#xff0c;程序在开发的时候&#xff0c;程序逻辑也变得越来越清晰。但是RTOS因为体量比较大&#xff0c;在一些内存比较小的MCU中无法应用。所以&#xff0c;在裸机的开发中&#xff0c;通常是使用一个while(1)来作为整个程序的大循环。当有条件需要执…

Vue路由传递query参数的两种方式

路由是可以传递参数的&#xff0c;一般使用query进行传参&#xff0c;有两种方式&#xff0c;接下来一一展示 案例展示 先编写一个简单的案例 我这里用的一个三级路由 这里使用三级路由以及展示路由视图 这样点击就跳转对应的路径了&#xff0c;接下来进行路由跳转的时候传参…

python之selenium入门教程

selenium&#xff0c;一个第三方库&#xff0c;可以通过给driver发送命令来操作浏览器&#xff0c;以达到模拟人操作浏览器&#xff0c;实现网页自动化、测试等&#xff0c;减少了重复性工作。 selenium的工作的基本架构如下&#xff1a; 安装 本文是在python环境下使用sele…

聚观早报 | 保时捷回应12.4万帕纳梅拉遭抢购;英特尔大规模降薪

今日要闻&#xff1a;保时捷回应12.4万帕纳梅拉遭抢购&#xff1b;特斯拉大力生产4680电池和Semi电动重卡&#xff1b;Spotify 月活用户预计下季度将达5亿里程碑&#xff1b;PayPal将裁员约2000人约占员工总数7%&#xff1b;英特尔大规模降薪 CEO基本薪酬削减25%保时捷回应12.4…

MATLAB应用3——深度视觉 奥比中光Astra S显示RGB和深度信息

首先从官网下载OpenNI驱动并安装&#xff0c;以及添加环境变量。MATLAB代码&#xff1a;% 参考&#xff1a;https://blog.csdn.net/limingmin2020/article/details/109445787%% 首次使用需编译mxNI.cpp文件&#xff0c;生成mxNI.mexw64mex mxNI.cpp -IF:\VS2017\VC\Astra_S\Ope…

Linux 内核代码审查人员短缺问题解决方法

导读操作系统安全是现在最重要的事情&#xff0c;而 Linux 则是一个主要被讨论的部分。首先要解决的问题之一就是&#xff1a;我们如何确定提交到上游的补丁已经进行了代码审核&#xff1f; Wolfram Sang 从 2008 年开始成为一名 Linux 内核开发者&#xff0c;他经常在各地召开…

Java三大特性之二——继承(工作、面试、学习必备技能)

目录 继承的概述 继承的特点 继承中的成员变量 继承中的成员方法 方法的重写 继承中的构造方法 super关键字 super和this的区别 继承的概述 多个类中存在相同属性&#xff08;成员变量&#xff09;和行为&#xff08;方法&#xff09;时&#xff0c;将这些内容抽取到单独一…

【自学Python】Python字符串以某个字符开始或结尾

大纲 Python字符串开头 Python字符串开头教程 在开发过程中&#xff0c;很多时候我们需要判断一个 字符串 是否以某个字符或者是否以某个字符串开始的需求&#xff0c;在 Python 中&#xff0c;判断某个字符串是否以某个字符或者是否以某个字符串开头的函数为 startswith() 。…

TFAPI使用2.0建议

2.5 TFAPI使用2.0建议 学习目标 目标 无应用 无 2.5.2 TF2.0最新架构图 饱受诟病TF1.0的API混乱 删除 queue runner 以支持 tf.data。删除图形集合。API 符号的移动和重命名。tf.contrib 将从核心 TensorFlow 存储库和构建过程中移除 TensorFlow 2.0 将专注于 简单性 和 易用…

【Unity VR开发】结合VRTK4.0:忽略某一层级

介绍&#xff1a; 由前面学习可知&#xff1a; 对象指针将与任何包含碰撞体&#xff08;甚至是触发器碰撞体&#xff09;的游戏对象发生冲突&#xff0c;但有时我们希望对象指针完全忽略游戏对象&#xff0c;就好像它不在场景中一样。 例如&#xff0c;如果我们手里拿着一个…