当前位置: 首页 > news >正文

数据结构篇—栈(stack)

一、引入


        在数学史上有这样一个经典问题——汉诺塔问题。

        通过动图演示我们发现每一个圆片的运动是什么样的呢?

        我们发现,第一个放入的最大圆片将位于整个塔的最底端。所以若想将最大圆片拿出来,就得将压在它身上的所有圆片先按顺序取出才能将它移出。这就出现了这样一个先入后出的现象,也就是栈的基本运行逻辑

        接下来,就让我们一起来学习栈吧!

二、栈


2.1、栈的构成


通过引入,我们明白了栈是怎么进行数据的储存的。那么接下来就该介绍栈是怎么构成的啦!

先来认识认识栈的二要素吧。

栈一般由栈顶、栈底构成。就跟叠乐高一样。一层层堆叠下去。具体的样子如下图所示

         是不是就像一个盒子,你往里面一个一个的放东西,最下面的那个就叫栈底,最上面那个就叫栈顶,而当这个盒子里面什么东西都没有的情况就叫做空栈。

        下面使用代码构建栈这一数据结构的过程。

#define MAXSIZE=50 //首先,得先确定一个大盒子的容量,也就是分配空间的大小;
typedef int SElemType;//自定义一个int类型的数据类型。
typedef struct{
    SElemType data[MAXSIZE];//用于存放需要储存的数据
     SElemType *top;//设置栈顶
}SqStack;

        下面是有关于栈的一些函数。它们是怎么被构造出来的大家就接着往下看吧。

操作函数功能
InitStack(&S)初始化一个空栈
StackEmpty(S)判断一个栈是否为空栈
Push(&S,x)进栈,将x插入使之成为新栈顶
Pop(&S,&x)出栈,弹出栈顶元素,并用x返回
GetTop(S,&x)读取栈顶元素,并用x返回
Destory(&S)销毁栈S,并释放其空间。
ClearStack(&S)将栈S清除为空栈
StackLength(S)返回S的元素个数,即栈的长度
StackTraverse(S)从栈底到栈顶依次对S的每个元素进行访问

2.2、栈的基本操作


        2.2.1、栈的初始化

        我们知道了栈需要一个大盒子来充当装小盒子的空间,而小盒子就用来储存我们的数据。这时候我们需要一个确定栈顶的标志来辅助栈的运算所以我们栈的初始化就是整个框架的搭建。来看看代码吧!

void InitStack(SqStack *S){
    S->top=-1;//由于数据的排放中0是第一位。使用用-1表示数据为空
}

        2.2.2、空栈的判断

        想必大家已经知道了空栈是如何定义的。当一个栈是空栈的时候,栈顶是不存在的,所以我们定义的栈顶top就是-1。所以反过来说,如果我们的top为-1那这个栈就是空栈。

Status StackEmpty(SqStack S){
    if(S.top==-1){
        return OK;      //栈空
    }else{
        return ERROR;
    }
}

        2.2.3、栈的入栈

        我们知道当往大盒子里放入了一个物体的时候我们盒子内部的“高度”就会随着增加。而我们的栈顶也会变大。所以当我们放入一个新的栈元素之后我们的栈顶就会变为新加入的那个元素。而我们标记栈顶的指针top也会随之指向新元素。这样我们就完成了一次入栈操作。

Status Push(SqStacdk *S,ElemType e){
    //首先判断此栈是否为满栈
    if(S->top==MAXSIZE-1){
        return ERROR;
    }
    S->top++;    //栈顶元素加一
    S->data[S->top]==e;    //将新元素赋值给栈顶空间
    return OK;
}
    

        2.2.4、栈的出栈

        同入栈操作一样。关键点在于栈顶和标记栈顶的指针指向的元素的变化。而入栈的操作是“高度”变高,top++;那么反过来出栈是不是就是“高度”减小,top--呢?大家仔细思考一下看看代码和图解吧!

Status Pop(SqStacdk *S,ElemType *e){
    //首先判断此栈是否为空栈
    if(S->top==-1){
        return ERROR;
    }
    *e=S->sata[S->top];//将拿出来的值赋值给e
    S->top--;    //栈顶元素减一
    return OK;
}
    

        2.2.5、读取栈顶的元素

        当我们需要读取栈顶元素的时候需要怎么做呢?想必大家在了解了栈的结构之后一看,栈不是基于结构体的嘛,而且又有对应的指针指代,那就直接用->就可以访问了对吧!

Status GetTop(SqStack S,ElemType *e){
    //判断是否为空栈
    if(S->top==-1){
        return ERROR;
    }
    *e=S->data[S->top];
    return OK;
}

3、共享栈


3.1、共享栈的定义与构造


        来看下面这样一张图:

        两个人对着一个两端开口的管子吹气,将整个空间充满。而其中的虫子则是来回移动。现在我们有一个栈,并给它赋予50大小的空间。这时候,我们如果将最左边序号为0的空间看作栈底0,最右边序号为49的空间看作栈底1.这样当两个栈各自进行入栈操作后就会相遇对吧,最后将整个栈空间填满,而两个栈的分界点就是图中虫子的位置。所以我们得到了这样一张图。

用代码构造如下:


#define MAXSIZE 50  //给栈规定空间大小
typedef int ElemType;   //自定义int类型的数据类型ElemType

typedef struct{
	ElemType data[MAXSIZE];
	int top0;	//栈0栈顶
	int top1;	//栈1栈顶
}SqDoubleStack;

 3.2、共享栈的进栈与出栈


        共享栈的出入栈的操作相当于将两个普通栈的出入栈操作放到了同一个空间容器中且栈顶彼此相互靠近。

代码表示如下:

//共享栈的入栈

Status Push(SqDoubleStack *S, Elemtype e, int stackNumber){
    //判断是否为满栈
    if(S->top0+1 == S->top1){   
        return ERROR;
    }
//用if;else语句判断元素所需要进入的栈
    if(stackNumber == 0){   //栈0有元素进栈
        S->data[++S->top0] = e; //若栈0则先top0+1后给数组元素赋值
    }else if(satckNumber == 1){ //栈1有元素进栈
        S->data[--S->top1] = e; //若栈1则先top1-1后给数组元素赋值
    }
    return OK;
}

//共享栈的出栈
Status Pop(SqDoubleStack *S, ElemType *e, int stackNumber){
//先判断需要进行操作的是哪个栈。在进行空栈的判定。最后在进行出栈操作
    if(stackNumber == 0){
        if(S->top0 == -1){
            return ERROR;   //说明栈0已经是空栈,溢出
        }
        *e = S->data[S->top0--]; //将栈0的栈顶元素出栈,随后栈顶指针减1
    }
    else if(stackNumber == 1){
        if(S->top1 == MAXSIZE){
            return ERROR;   //说明栈1是空栈,溢出
        }
        *e = S->data[S->top1++];    //将栈1的栈顶元素出栈,随后栈顶指针加1
    }
    return OK;
}

4、链栈


4.1、链栈的表示与实现


        链栈是指采用链式存储结构实现的栈。通常采用单链表来表示。对于链栈的理解其实也很简单。普通的栈采用的是顺序结构,栈与栈之间的地址是紧密相连的。而链栈的分布较为散乱,彼此之间通过指针相连。

        在此使用StackNode表示,定义如下:

typedef struct StackNode{
    ElemType data;
    struct StackNode *next;
}StackNode,*LinkStack;

由于栈的主要操作是在栈顶插入和删除,所以用链表的头部作为栈顶是最方便的.

4.2、链栈的基本操作


4.2.1、链栈的初始化

        链栈的初始化就是构造一个空栈,所以直接将栈顶指针设置为NULL就好了

代码描述

4.2.2、链栈的入栈和出栈

        链栈的入栈和单链表的头部插入是大相径庭的。主要有以下几个步骤:

a、为入栈元素e分配空间,用指针p指向。

b、将新结点数据的内容部分(数据域)设置为e

c、将新结点插入栈顶

d、修改栈顶指针为p

代码描述:

Status Push(SqStacdk *S,ElemType e){
    //在栈顶插入元素e
    p=new StackNode;//
    p->data=e;//将新节点中的内容部分设置为e
    p->next=S;//将新节点插入栈顶
    S=p;//修改栈顶指针为p
    return OK;
}
    

        链表的出栈和顺序栈一样,在进行出栈操作前要先判断是否为空栈。但是链栈出栈后需要释放掉出栈元素所在的栈顶空间。

主要有以下五步:

a、判断栈是否为空,空则返回ERROR

b、将栈顶元素赋值给e

c、临时保存栈顶元素空间,以备释放

d、修改栈顶指针,指向新的栈顶元素

e、释放原栈顶元素的空间。

代码描述:

Status Pop(SqStacdk *S,ElemType *e){
    //首先判断此栈是否为空栈
    if(S==NULL){
        return ERROR;
    }
    *e=S->data[S->top];//将拿出来的值赋值给e
    p=S;//用p临时保存栈顶元素空间,以备释放
    S=S->next;//修改栈顶指针
    deleat p;//释放原栈顶空间。
    return OK;
}
    

4.2.3、取栈顶元素

        和顺序栈一样,当栈非空时,此操作返回当前栈顶元素的值,栈顶指针S不变。

算法描述:

elemType GetTop(LinkStack S){
    //返回S的栈顶元素,不修改栈顶指针
    if(S!=NULL){//栈非空
        return S->date;//返回栈顶元素
    }
}

         如果我的内容对你有帮助,在下就厚着脸皮讨个点赞关注。如果你有更好的想法,还望留在评论区让我来参考学习。我将不胜感激并努力创作出更好的内容。

相关文章:

  • 蓝陵科技:以“数字底座”之力,全面布局影视行业工业化
  • 【赵渝强老师】监控Redis
  • Redis100道高频面试题
  • 初阶数据结构习题【7】(3顺序表和链表)—— 21. 合并两个有序链表
  • 浅谈汽车系统电压优缺点分析
  • 2.Deepseek提示语技巧到多场景应用
  • 《AI大模型技术应知应会100篇》No1. 大模型文件格式一篇读懂读全
  • linux网络(3)—— socket编程(1)socket基础认识
  • 一篇文了解 Go的 init 函数---目的、执行和最佳实践
  • 【大模型学习】第三章 Prompt工程技术要点详解:从原理到应用
  • 如何实现在Windows系统内搭建Linux系统(WSL)
  • FPGA学习篇——Verilog学习3
  • 记录遇到的面试题
  • 云服务器漏洞 ALINUX3-SA-2024:0050: container-tools:rhel8 安全和BUG修复更新
  • Jmeter日志打印
  • 某小说网站爬虫
  • Hessian 矩阵的正定性与目标函数凸性的证明
  • 第52天:Web开发-JavaEE应用SpringBoot栈SnakeYaml反序列化链JARWAR构建打包
  • 【FSM-3: 串行序列】
  • 40岁开始学Java:Java中ASCII和Unicode的区别
  • 电子商务网站有哪几种/软文范例200字
  • 网站风格主要包括/网站seo谷歌
  • 高端公司网站建设/搜索引擎seo关键词优化
  • bootstrap个人网站模板/徐州百度seo排名
  • 代码网站推荐/百度搜索推广平台
  • 动画设计工资/长沙谷歌优化