数据结构 之 栈(Stack)

​​​​​​​

🎉欢迎大家观看AUGENSTERN_dc的文章(o゜▽゜)o☆✨✨

🎉感谢各位读者在百忙之中抽出时间来垂阅我的文章,我会尽我所能向的大家分享我的知识和经验📖

🎉希望我们在一篇篇的文章中能够共同进步!!!

🌈个人主页:AUGENSTERN_dc

🔥个人专栏:C语言 | Java | 数据结构

⭐个人格言:

一重山有一重山的错落,我有我的平仄

一笔锋有一笔锋的着墨,我有我的舍得

目录

1. 定义:

2. 栈、虚拟机栈、栈帧的区别: 

3.栈的常用方法和模拟实现:

3.1 常用方法:

3.2 模拟实现:

4. 模拟实现全部源码:

5. 栈的应用场景:


1. 定义:

栈是一种特殊的线性表,只允许在固定的一端进行插入和删除元素操作

进行数据插入的和删除的一端称为栈顶,另一端称为栈底;

栈这个数据结构遵从后进先出(先进后出)的原则;

如图所示,每次在栈中添加元素或者取出元素时,只能在栈顶进行操作,这就是后进先出的原则

类似于我们现实生活中的枪械的弹夹一般:

先装入弹夹的子弹,往往在弹夹的最下方,同时也是最后一个被发射出去的;

又例如:

这是我们qq的更新的弹窗,如果我们不关闭这个弹窗,就无法使用qq的其他功能,这个弹窗是最后一个弹出来的,同时也是第一个被关闭的,这里同样使用的栈这个数据结构;

从上图可以看出,Stack继承于Vector,Vevtor和ArrayList类似,都是动态的顺序表,不同的是Vector是线程安全的;

2. 栈、虚拟机栈、栈帧的区别: 

栈:如上所述,栈是一种后进先出的数据结构;

虚拟机栈:虚拟机栈是具有特殊作用的一块内存空间;

                  jvm为了对数据进行更好的管理,将内存按照不同的需求划分出来的一种结构

栈帧:一种结构,这种结构与函数调用相关的,内部:局部变量表、操作数栈......

           每个方法在运行时,jvm都会创建一个栈帧,然后将栈帧压入到虚拟机栈中

           当方法调用结束时,该方法对应的栈帧会从虚拟机栈中出栈

3.栈的常用方法和模拟实现:

3.1 常用方法:

方法作用
Stack()构造一个大小为默认大小的栈
Stack(int n)

构造一个大小为n的空栈

E push(E e)将e入栈,并返回e
E pop()        获取栈顶元素并删除栈顶元素
E peek()获取栈顶元素
size()获取栈的元素个数
empty()        检测栈是否为空

 (在栈的模拟实现中,我们并不使用泛型,而是使用整形来代替泛型)

  以上就是栈的常用方法,接下来我们开始模拟实现这些方法:

3.2 模拟实现:

首先我们来写一个My_Stack类:

public class My_Stack {

    int[] elem;                     //模拟实现栈
    int usedSize;                   //已使用的长度
    int DEFAULT_CAPACITY = 10;      //默认大小
}

< 1 > 构造方法:

这里模拟实现两个构造方法:

一个是构造默认大小的栈

一个是构造指定大小的栈

public My_Stack() {
        this.elem = new int[DEFAULT_CAPACITY];      //构造一个大小为默认大小的栈
}

    public My_Stack(int n) {
        this.elem = new int[n];                     //构造一个大小为n的栈
}

< 2 > push方法:

push方法是将给定的值进行入栈操作,并返回该值

在进行push方法的时候,我们需要判断栈是否满了,如果栈满了,我们就需要进行扩容,那我们就需要写一下判断栈是否满了和扩容的方法:

private boolean isFUll () {
        return usedSize == elem.length;     //返回数组长度是否等于使用大小 的值
}

private void grow (int[] elem) {
        if (elem.length <= 64) {
            elem = Arrays.copyOf(elem, elem.length * 2);        //如果数组长度小于64,则进行二倍扩容
        } else {
            elem = Arrays.copyOf(elem, elem.length + elem.length >> 1);  //如果大于64,则进行1.5倍扩容
        }
}

 在此基础上,我们开始push方法的模拟实现:

public int push (int val) {
        if (isFUll()) {                 //判断栈是否满了,如果满了,则进行扩容
            grow(elem);
        }
        elem[usedSize++] = val;         //在数组的最末尾进行插入,并将usedSize++
        return val;                     //返回val的值
}

< 3 > empty方法:

empty方法是判断栈是否为空,方法比较简单,如下:

public boolean empty () {
        return usedSize == 0;       //判断usedSize是否 = 0 即可
    }

< 4 > pop方法:

pop方法是将栈顶元素进行出栈操作,并返回该值:

在进行该操作的时候,我们同样需要判断,栈是否为空,若栈为空,则抛出异常:

异常的代码如下:

public class StackEmptyException extends RuntimeException{
    public StackEmptyException () {
        super();
    }

    public StackEmptyException (String str) {
        super(str);
    }
}

随后进行pop方法的模拟实现:

public int pop () {
        if (empty()) {
            throw new StackEmptyException("该栈为空,不能进行pop操作!!!");
        }
        int ret = elem[usedSize];       //将elem[usedSize]的值保存下来
        usedSize--;                     //usedSize--相当于删去数组最后一位元素
        return ret;                     //返回被删掉的元素
}

< 5 > peek方法:

peek方法是返回栈顶的元素,但不删除栈顶元素:

同样在使用peek方法时,我们需要判断栈是否为空,若为空,则需要抛出异常:

public int peek () {
        if (empty()) {          //判断是否为空
            throw new StackEmptyException("该栈为空,不能进行peek操作!!!");
        }
        return elem[usedSize - 1];          //将栈顶元素进行返回
}

< 6 > size方法:

public int size () {
        return usedSize;        //直接返回usedSize大小
}

4. 模拟实现全部源码:

import java.util.Arrays;

public class My_Stack {

    int[] elem;                     //模拟实现栈
    int usedSize;                   //已使用的长度
    int DEFAULT_CAPACITY = 10;      //默认大小

    public My_Stack() {
        this.elem = new int[DEFAULT_CAPACITY];      //构造一个大小为默认大小的栈
    }

    public My_Stack(int n) {
        this.elem = new int[n];                     //构造一个大小为n的栈
    }

    public int push (int val) {
        if (isFUll()) {                 //判断栈是否满了,如果满了,则进行扩容
            grow(elem);
        }
        elem[usedSize++] = val;         //在数组的最末尾进行插入,并将usedSize++
        return val;                     //返回val的值
    }

    private boolean isFUll () {
        return usedSize == elem.length;     //返回数组长度是否等于使用大小 的值
    }

    private void grow (int[] elem) {
        if (elem.length <= 64) {
            elem = Arrays.copyOf(elem, elem.length * 2);        //如果数组长度小于64,则进行二倍扩容
        } else {
            elem = Arrays.copyOf(elem, elem.length + elem.length >> 1);  //如果大于64,则进行1.5倍扩容
        }
    }

    public int pop () {
        if (empty()) {
            throw new StackEmptyException("该栈为空,不能进行pop操作!!!");
        }
        int ret = elem[usedSize];       //将elem[usedSize]的值保存下来
        usedSize--;                     //usedSize--相当于删去数组最后一位元素
        return ret;                     //返回被删掉的元素
    }

    public boolean empty () {
        return usedSize == 0;       //判断usedSize是否 = 0 即可
    }

    public int peek () {
        if (empty()) {          //判断是否为空
            throw new StackEmptyException("该栈为空,不能进行peek操作!!!");
        }
        return elem[usedSize - 1];          //将栈顶元素进行返回
    }

    public int size () {
        return usedSize;        //直接返回usedSize大小
    }
}

5. 栈的应用场景:

1. 改变元素的序列

例如,将1  2  3  4依次进行入栈操作,期间可以进行出栈操作,那么我们可以得到许多出栈序列;

2. 将递归转换成循环

例如逆序打印链表

// 递归方式
void printList(Node head){
    if(null != head){
        printList(head.next);
        System.out.print(head.val + " ");
    }
 }
 
// 循环方式
void printList(Node head){
    if(null == head){
        return;
    }
    
    Stack<Node> s = new Stack<>();
    // 将链表中的结点保存在栈中
    Node cur = head;
    while(null != cur){
        s.push(cur);
        cur = cur.next;
    }

下面我为大家推荐几道关于栈的题,有兴趣的朋友可以去练练手:

1. 括号匹配

2. 逆波兰表达式求值

3. 出栈入栈次序匹配

4. 最小栈

以上就是栈的全部内容,感谢大家观看!!!!!

制作不易,三连支持

谢谢!!!

以上的模拟实现代码未必是最优解,仅代表本人的思路,望多多理解,谢谢!!

最后送给大家一句话,同时也是对我自己的勉励:

夜色难免黑凉,前行必有曙光!!!

  • 29
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值