首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

数据结构 - 栈

编程知识
2024年10月13日 23:13

栈一种常见的特殊线性数据结构,其特殊之处在于其操作顺序,下面会详细介绍,也正因为其特性,因此栈可以轻松解决表达式求值、括号匹配、递归算法、回溯算法等等问题。

01、定义

栈的特殊性表现为操作受限,其一只允许在栈的一端进行元素插入和删除运算,其二栈的运算操作遵循后进先出(Last In First Out,简称LIFO)的原则。

入栈:当把元素插入到栈,这一行为叫做入栈,也称进栈或压栈;

出栈:当把元素从栈中移除,这一行为叫做出栈,也称退栈;

栈顶:允许元素进行插入和删除操作的一端称为栈顶;

栈底:与栈顶对应的另一个端称为栈底,并且不允许进行元素操作;

空栈:当栈中没有元素时叫做空栈。

满栈:当栈是有限容量,并且容量已用完,则称为满栈。

栈容量:当栈是有限容量,栈容量表示栈可以容纳的最大元素数量。

栈大小:表示当前栈中的元素数量。

02、分类

栈是逻辑结构,因此以存储方式的不同可以分为顺序栈和链栈。

顺序栈就是使用连续的地址空间存储所有栈元素,通常采用数组实现,因此导致栈的大小是固定的,不易扩扩容,容易浪费空间同时还要注意元素溢出等问题。

链栈顾名思义就是采用链式方式存储,通常采用单向链表实现,因此链栈可以无限扩容,按需使用,内存利用高效,同时也不存在满栈的情况。

03、实现(顺序栈)

我们借助数组来实现顺序栈,其核心思想是把数组的起始位置作为栈底,把数组尾方向当作栈顶。

我们知道数组对插入、删除元素是不友好的,因为涉及到已存在元素移动的问题,但是如果直接在数组尾端插入、删除元素还是很方便的,因为不涉及元素移动问题,我们正是利用这一特点,把数组起始位置做为栈底,而插入、删除方便的数组尾端作为栈顶。

下面我们将一步一步实现一个泛型顺序栈。

1、ADT定义

我们首先来定义顺序栈的ADT。

ADT Stack{

数据对象:D 是一个非空的元素集合,D = {a1, a2, ..., an},其中 ai 表示栈中的第i个元素,n是栈的长度。

数据关系:D中的元素通过它们的索引(位置)进行组织,索引是从0到n-1的整数,并且遵循元素只能在栈顶操作,元素后进先出的原则。

基本操作:[

Init(n) :初始化一个指定容量的空栈。

Capacity:返回栈容量。

Length:返回栈长度。

Top:返回栈顶元素,当为空栈则报异常。

IsEmpty():返回是否为空栈。

IsFull():返回是否为满栈。

Push():入栈即添加元素,当为满栈则报异常。

Pop():出栈即返回栈顶元素并把其从栈中移除,当为空栈则报异常。

]

}

定义好栈ADT,下面我们就可以开始自己实现的栈。

2、初始化Init

初始化结构主要做几件事。

  • 初始化栈的容量;

  • 初始化存放栈元素数组;

  • 初始化栈顶索引;

具体实现代码如下:

//存放栈元素
private T[] _array;
//栈容量
private int _capacity;
//栈顶索引,为-1表示空栈
private int _top;
//初始化栈为指定容量
public MyselfArrayStack<T> Init(int capacity)
{
    //初始化栈容量为capacity
    _capacity = capacity;
    //初始化指定长度数组用于存放栈元素
    _array = new T[_capacity];
    //初始化为空栈
    _top = -1;
    //返回栈
    return this;
}

3、获取栈容量 Capacity

这个比较简单直接把栈容量私有字段返回即可。

//栈容量
public int Capacity
{
    get
    {
        return _capacity;
    }
}

4、获取栈长度 Length

因为栈顶索引表示数组下标,因此可以通过栈顶索引加1转为栈长度,同时因为我们定义了空栈是栈顶索引为-1,因此此时栈长等于[-1+1]为0,所以栈长度即为[栈顶索引+1]。

//栈长度
public int Length
{
    get
    {
        //栈长度等于栈顶元素加1
        return _top + 1;
    }
}

5、获取栈顶元素 Top

获取栈顶元素可以通过栈顶索引私有字段从数组中直接获取,同时要注意判断栈是否为空栈,如果为空栈则报异常。具体代码如下:

//获取栈顶元素
public T Top
{
    get
    {
        if (IsEmpty())
        {
            //空栈,不可以进行获取栈顶元素操作
            throw new InvalidOperationException("空栈");
        }
        return _array[_top];
    }
}

6、获取是否空栈 IsEmpty

是否空栈只需判断栈顶索引是否为小于0即可。

//是否空栈
public bool IsEmpty()
{
    //栈顶索引小于0表示空栈
    return _top < 0;
}

7、获取是否满栈 IsFull

是否满栈只需判断栈顶索引是否与栈容量减1相等,代码如下:

//是否满栈
public bool IsFull()
{
    //栈顶索引等于容量大小表示满栈
    return _top == _capacity - 1;
}

8、入栈 Push

入栈则是在把栈顶索引向数组后移动一位后,再把新元素赋值到栈顶索引所对应的元素上,同时还需要检查是否为满栈,如果是满栈则报错,具体实现代码如下:

//入栈
public void Push(T value)
{
    if (IsFull())
    {
        //栈顶索引大于等于容量大小减1,表明已经满栈,不可以进行入栈操作
        throw new InvalidOperationException("满栈");
    }
    //栈顶索引先向后移动1位,然后再存放栈顶元素
    _array[++_top] = value;
}

9、出栈 Pop

出栈则是首先取出栈顶元素后,然后把栈顶索引向数组前移动一位,最后返回取出的栈顶元素,同时还需要检查是否为空栈,如果是空栈则报错,具体实现代码如下:

//出栈
public T Pop()
{
    if (IsEmpty())
    {
        //栈顶索引小于1表示空栈,不可以进行出栈操作
        throw new InvalidOperationException("空栈");
    }
    //返回栈顶元素后,栈顶索引向前移动1位
    return _array[_top--];
}

04、实现(链栈)

我们借助链表来实现链栈,其核心思想是把链表尾节点作为栈底,把链表首元节点当作栈顶。

这是因为如果我们想拿到链表的尾节点需要变量整个链表才可以拿到,但是要想获取首元节点则可以通过头指针直接获取到(无头节点链表),因此对于链表尾节点来说操作时不友好的适合来做栈底,链表首元节点对操作友好适合做为栈顶。

下面我们将一步一步实现一个泛型链栈。

1、ADT定义

相对于顺序栈的ADT来说,链栈的ADT少了两个方法即获取栈容量和是否满栈,这也是链表特性带来的好处。

2、初始化Init

初始化结构主要初始化栈顶节点为空和栈长度为0,具体实现如下:

public class MyselfStackNode<T>
{
    //数据域
    public T Data;
    //指针域,即下一个节点
    public MyselfStackNode<T> Next;
    public MyselfStackNode(T data)
    {
        Data = data;
        Next = null;
    }
}
public class MyselfStackLinkedList<T>
{
    //栈顶节点即首元节点
    private MyselfStackNode<T> _top;
    //栈长度
    private int _length;
    //初始化栈
    public MyselfStackLinkedList<T> Init()
    {
        //初始化栈顶节点为空
        _top = null;
        //初始化栈长度为0
        _length = 0;
        //返回栈
        return this;
    }
}

3、获取栈长度 Length

这个比较简单直接把栈长度私有字段返回即可。

//栈长度
public int Length
{
    get
    {
        return _length;
    }
}

4、获取栈顶元素 Top

获取栈顶元素可以通过栈顶节点直接获取,但是要注意判断栈是否为空栈,如果为空栈则报异常。具体代码如下:

//获取栈顶元素
public T Top
{
    get
    {
        if (IsEmpty())
        {
            //空栈,不可以进行获取栈顶元素操作
            throw new InvalidOperationException("空栈");
        }
        //返回首元节点数据域
        return _top.Data;
    }
}

5、获取是否空栈 IsEmpty

是否空栈只需判断栈顶节点是否为空即可。

//是否空栈
public bool IsEmpty()
{
    //栈顶节点为null表示空栈
    return _top == null;
}

6、入栈 Push

入栈则是首先创建一个新节点,然后把原栈顶节点赋值给新节点的指针域,最后把新节点变更为栈顶节点,同时栈长加1,具体实现代码如下:

//入栈
public void Push(T value)
{
    //创建新的栈顶节点
    var node = new MyselfStackNode<T>(value);
    //将老的栈顶节点赋值给新节点的指针域
    node.Next = _top;
    //把栈顶节点变更为新创建的节点
    _top = node;
    //栈长度加1
    _length++;
}

7、出栈 Pop

出栈则是首先取出栈顶节点对应的数据后,然后把栈顶节点指向原栈顶节点对应的下一个节点,同时栈长度减1,当然如果空栈则报错,具体实现代码如下:

//出栈
public T Pop()
{
    if (IsEmpty())
    {
        //空栈,不可以进行出栈操作
        throw new InvalidOperationException("空栈");
    }
    //获取栈顶节点数据
    var data = _top.Data;
    //把栈顶节点变更为原栈顶节点对应的下一个节点
    _top = _top.Next;
    //栈长度减1
    _length--;
    //返回栈顶数据
    return data;
}

:测试方法代码以及示例源码都已经上传至代码库,有兴趣的可以看看。https://gitee.com/hugogoos/Planner

From:https://www.cnblogs.com/hugogoos/p/18462867
本文地址: http://shuzixingkong.net/article/2467
0评论
提交 加载更多评论
其他文章 JavaScript原型链污染探讨
如果你想弄明白什么怎样才可以实现JavaScript的原型链污染,那么你首先需要弄清楚两个东西,那就是__proto__和prototype。 到底什么才是__proto__和prototype? 那我们先来看看比较官方的说法吧: __proto__:是每个对象的隐藏属性,指向创建该对象的构造函数的
JavaScript原型链污染探讨
(系列六).net8 全局异常捕获机制
说明 该文章是属于OverallAuth2.0系列文章,每周更新一篇该系列文章(从0到1完成系统开发)。 该系统文章,我会尽量说的非常详细,做到不管新手、老手都能看懂。 说明:OverallAuth2.0 是一个简单、易懂、功能强大的权限+可视化流程管理系统。 友情提醒:本篇文章是属于系列文章,看该
(系列六).net8 全局异常捕获机制 (系列六).net8 全局异常捕获机制 (系列六).net8 全局异常捕获机制
如何在kubernetes环境中共享GPU
随着人工智能和大模型的快速发展,云上GPU资源共享变得必要,因为它可以降低硬件成本,提升资源利用效率,并满足模型训练和推理对大规模并行计算的需求。 在kubernetes内置的资源调度功能中,GPU调度只能根据“核数”进行调度,但是深度学习等算法程序执行过程中,资源占用比较高的是显存,这样就形成了很
如何在kubernetes环境中共享GPU 如何在kubernetes环境中共享GPU
让查询可以使用 json path
记录一下最近sv.db的完善 1. 让查询可以使用 json path 有时候我们会存储 json 到 db,也有时会只取json部分数据,或者通过json部分数据进行过滤 所以sv.db 也支持这些场景,(目前只有 db 实现,json的操作都是依靠db json 函数) 举例: 数据 a.Exe
Spark任务OOM问题如何解决?
大家好,我是 V 哥。在实际的业务场景中,Spark任务出现OOM(Out of Memory) 问题通常是由于任务处理的数据量过大、资源分配不合理或者代码存在性能瓶颈等原因造成的。针对不同的业务场景和原因,可以从以下几个方面进行优化和解决。 一、业务场景及可能的OOM原因分析 数据量过大: 业务场
Nuxt3+PM2集群模式启动及勘误
起因 之前写过一篇 Nuxt3 的文章,Nuxt3 环境变量配置,用到了 PM2,但是里面的一些配置存在问题,最近有空又验证了一下,这里做一个勘误。 问题 PM2 的启动配置中有一项是exec_mode,默认是fork,另一个可选值是cluster,fork 是单进程模式,cluster 是多进程模
Nuxt3+PM2集群模式启动及勘误 Nuxt3+PM2集群模式启动及勘误 Nuxt3+PM2集群模式启动及勘误
Android 车载应用开发指南 - CAN Bus 协议详解
​ 在现代车载应用开发中,CAN(Controller Area Network)总线协议扮演着不可或缺的角色。作为一个汽车内部网络的标准协议,CAN Bus 已经成为了车载系统通信的基础。而在 Android 车载应用开发的过程中,理解并利用好 CAN Bus 协议是必不可少的。 那么,CAN B
Android 车载应用开发指南 - CAN Bus 协议详解 Android 车载应用开发指南 - CAN Bus 协议详解 Android 车载应用开发指南 - CAN Bus 协议详解
apisix~自定义文件上传代理插件~支持form-data文件和kv参数
参考文献 https://stackoverflow.com/questions/24535189/composing-multipart-form-data-with-a-different-content-type-on-each-parts-with-j https://www.reddit.