栈 之 如何实现一个栈

简介: 栈 之 如何实现一个栈

前言

栈最鲜明的特点就是后进先出,一碟盘子就是类似这样的结构,最晚放上去的,可以最先拿出来。本文将介绍的是如何自己实现一个栈结构。

栈的操作

栈是一种先进后出(Last-In-First-Out, LIFO)的数据结构,常见操作包括:

1、入栈(Push):将元素压入栈顶。

2、出栈(Pop):弹出栈顶元素并返回其值。

3、查看栈顶元素(Peek):返回栈顶元素的值,但不对栈进行修改。

4、判断栈是否为空(isEmpty):检查栈是否为空,如果栈中没有任何元素,则返回 true;否则返回 false。

5、获取栈的大小(size):返回栈中元素的数量。

6、清空栈(clear):清除栈中的所有元素,使其变为空栈。

这些是栈的基本操作。栈的实际使用还可能涉及其他操作,如遍历栈、搜索特定元素、栈的深度等等。根据具体的需求,你可以针对栈的特性来自定义其他更复杂的操作。

栈的实现

栈是较容易实现的抽象数据结构之一。我们可以选择数组或者链表来实现,它们各有特点,前者容量有限且固定,但操作简单,而后者容量理论上不受限,但是操作并不如数组方便,每次入栈要进行内存申请,出栈要释放内存,稍有不慎便造成内存泄露。本文对两种实现都做介绍。

1、用数组实现栈

数组之你值得了解的底层

/**
 * 用数组实现一个栈
 */
public class MyStack {
    private int maxSize;  //栈的大小
    private int[] stackArray; // 数组,模拟一个栈,用于存放
    private int top = -1; // 表示栈顶,初始为-1
    public MyStack(int maxSize) {
        this.maxSize = maxSize;
        stackArray = new int[this.maxSize];
    }
    public void push(int value) {
        //判断是否栈满
        if(isFull()){
            System.out.println("栈满了..");
            return;
        }
        stackArray[++top] = value;
    }
    //弹出栈顶的元素并返回
    public int pop(int value) {
        //判断栈是否为空
        if(isEmpty()) {
            throw new RuntimeException("栈空,没有数据~~");
        }
        value = stackArray[top--];
        return value;
    }
    //查看栈顶的元素
    public int peek() {
        //判断栈是否为空
        if(isEmpty()) {
            throw new RuntimeException("栈空,没有数据~~");
        }
        return stackArray[top];
    }
    // 显示栈中的数据-从栈顶开始显示
    public void list() {
        // 判断是否栈空
        if (isEmpty()) {
            System.out.println("栈空~~");
            return;
        }
        //从栈顶开始展示数据
        for (int i = top; i >= 0; i--) {
            System.out.printf("stack[%d]=%d\n", i, stackArray[i]);
        }
    }
    //栈空
    public boolean isEmpty() {
        return top == -1;
    }
    //栈满
    public boolean isFull() {
        return top == (maxSize - 1);
    }
}

2、用队列实现栈

Java两个队列实现一个栈

3、用链表实现栈

/**
 * @Description
 * @Author Flag
 * @Date: 2021/7/20 8:53
 * @Version: 1.0
 **/
public class LinkedStackDome {
    public static void main(String[] args) {
        //测试一下LinkedStack是否正常
        //先创建一个ArrayStack对象->表示栈
        LinkedStack stack = new LinkedStack(4);
        String key = "";
        boolean loop = true;//用于控制是否退出
        Scanner scanner = new Scanner(System.in);
        while (loop){
            System.out.println("show:表示显示栈");
            System.out.println("exit:退出程序");
            System.out.println("push:表示添加元素到栈(入栈)");
            System.out.println("pop:表示从栈取出数据(出战)");
            System.out.println("请输入你的选择:");
            key = scanner.next();
            switch (key){
                case "show":
                    stack.show();
                    break;
                case "exit":
                    scanner.close();
                    loop = false;
                    break;
                case "push":
                    System.out.println("请输入一个数字");
                    int i = scanner.nextInt();
                    stack.push(i);
                    break;
                case "pop":
                    try{
                        int pop = stack.pop();
                        System.out.println("出栈的数据:"+pop);
                    } catch (Exception e){
                        System.out.println(e.getMessage());
                    }
                    break;
                default:
                    break;
            }
        }
    }
}
class LinkedStack{
    //定义栈的大小
    private int maxSize;
    //链表模拟栈,
    private LinkedStackNode first;
    //top表示栈顶,初始化是-1
    private int top;
    /**
     * 构造方法
     * @param maxSize 栈的大小
     */
    public LinkedStack(int maxSize) {
        this.maxSize = maxSize;
        top = -1;
    }
    /**
     * 判断栈是否满了
     * @return
     */
    public boolean isFull(){
        return top+1 == maxSize;
    }
    /**
     * 入栈
     * @param value 入栈的元素
     */
    public void push(int value){
        //1、判断栈是否满了
        if(this.isFull()){
            System.out.println("栈已经满了,不能再添加元素");
            return;
        }
        //2、新创建一个节点,用于添加到链表上
        LinkedStackNode node = new LinkedStackNode(value);
        //3、将top++,表示链表中的元素新增了一个
        top++;
        //4、判断头元素是否为null,如果是null,代表是第一个元素,则直接让新元素当第一个元素
        if(first == null){
            first = node;
            return;
        }
        //5.如果头元素不是null,则证明,此时链表中已经有元素,则将新元素添加上即可
        //5.1获取到链表的尾部
        LinkedStackNode middleNode = first;
        while (middleNode.getNext() != null){
            middleNode = middleNode.getNext();
        }
        //5.2将链表添加上去
        middleNode.setNext(node);
    }
    /**
     * 出栈
     * @return 出栈的元素
     */
    public int pop(){
        //1.判断栈是否为null
        if(this.isEmpty()){
            throw new RuntimeException("栈中没有元素");
        }
        //2.将链表的数量减一
        top -- ;
        //3.如果链表中是否只有一个元素
        if(first.getNext() == null){
            LinkedStackNode popNode = this.first;
            this.first = null;
            return popNode.getNumber();
        }
        //4.如果链表中有不只一个元素
        //4.1.定义一个中间变量,让他指向链表的最后一个元素,即最后要出栈的元素
        LinkedStackNode lastNode = first;
        //4.2.定义一个中间变量,用来用来获取到比lastNode前一个元素,‘
        //因为是单向链表,我们出栈后,要置空指向最后一个元素的指针,所以需要找到最有一个元素的前一个元素进行操作
        LinkedStackNode beforeLastNode = null;
        //4.2.遍历链表,直到lastNode是最后一个元素,此时,如果链表中只有一个元素,则
        while (lastNode.getNext() != null){
            //将lastNode给到beforeLastNode
            //然后lastNode向后移动
            //此时就构造出 beforeLastNode在lastNode前一个位置的情况
            beforeLastNode = lastNode;
            lastNode = lastNode.getNext();
        }
        //4.3.此时将最后一个元素的前一个元素的next指针变成null,则相当于舍弃掉了最后一个元素
        beforeLastNode.setNext(null);
        //4.3.返回lastNode的编号
        return lastNode.getNumber();
    }
    /**
     * 显示栈的元素
     */
    public void show(){
        //判断栈是否为null
        if(this.isEmpty()){
            System.out.println("栈中无元素");
            return;
        }
        //定义一个新的链表节点
        LinkedStackNode newLinedStackHead = null;
        //正向遍历原始链表,将链表的每一个元素,都放到新的链表的第一个元素
        //因为前面做了判断,所以first不可以为null
        LinkedStackNode oldLinkedStackNode = first;
        //直到原始链表元素为null时,结束
        while (oldLinkedStackNode != null){
            LinkedStackNode middleNode = new LinkedStackNode(oldLinkedStackNode.getNumber());
            if(newLinedStackHead == null){
                newLinedStackHead = middleNode;
            } else {
                middleNode.setNext(newLinedStackHead);
                newLinedStackHead = middleNode;
            }
            //移动原始链表的位置
            oldLinkedStackNode = oldLinkedStackNode.getNext();
        }
        while (newLinedStackHead != null){
            System.out.println(newLinedStackHead.getNumber());
            newLinedStackHead = newLinedStackHead.getNext();
        }
    }
    /**
     * 判断栈是否为null
     * @return 结果
     */
    public boolean isEmpty(){
        return top == -1;
    }
}
/**
 * 链表栈的节点
 */
class LinkedStackNode{
    private int number;
    private LinkedStackNode next;
    public LinkedStackNode(int number) {
        this.number = number;
    }
    public int getNumber() {
        return number;
    }
    public LinkedStackNode getNext() {
        return next;
    }
    public void setNext(LinkedStackNode next) {
        this.next = next;
    }
}

栈的应用

1、栈的应用——递归

1)、递归的定义

递归是一种重要的程序设计方法。简单地说,若在一个函数、过程或数据结构的定义中又应用了它自身,则这个函数、过程或数据结构称为是递归定义的,简称递归。

它通常把一个大型的复杂问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的代码就可以描述岀解题过程所需要的多次重复计算,大大减少了程序的代码量但在通常情况下,它的效率并不是太高。

2)、斐波那契数列

2、栈的应用——四则运算表达式求值

1)、后缀表达式计算结果

2)、中缀表达式转后缀表达式

笔记

相关文章
|
5天前
|
算法 安全 测试技术
golang 栈数据结构的实现和应用
本文详细介绍了“栈”这一数据结构的特点,并用Golang实现栈。栈是一种FILO(First In Last Out,即先进后出或后进先出)的数据结构。文章展示了如何用slice和链表来实现栈,并通过golang benchmark测试了二者的性能差异。此外,还提供了几个使用栈结构解决的实际算法问题示例,如有效的括号匹配等。
golang 栈数据结构的实现和应用
01_设计一个有getMin功能的栈
01_设计一个有getMin功能的栈
|
5天前
|
前端开发
07_用队列实现栈
07_用队列实现栈
06_用栈来求解汉诺塔问题
06_用栈来求解汉诺塔问题
05_用一个栈实现另一个栈的排序
05_用一个栈实现另一个栈的排序
03_如何仅用递归函数和栈操作逆序一个栈
03_如何仅用递归函数和栈操作逆序一个栈
|
5天前
|
测试技术
02_由两个栈组成的队列
02_由两个栈组成的队列
|
9天前
|
存储
|
24天前
|
存储 人工智能 C语言
数据结构基础详解(C语言): 栈的括号匹配(实战)与栈的表达式求值&&特殊矩阵的压缩存储
本文首先介绍了栈的应用之一——括号匹配,利用栈的特性实现左右括号的匹配检测。接着详细描述了南京理工大学的一道编程题,要求判断输入字符串中的括号是否正确匹配,并给出了完整的代码示例。此外,还探讨了栈在表达式求值中的应用,包括中缀、后缀和前缀表达式的转换与计算方法。最后,文章介绍了矩阵的压缩存储技术,涵盖对称矩阵、三角矩阵及稀疏矩阵的不同压缩存储策略,提高存储效率。
|
26天前
|
存储 C语言
数据结构基础详解(C语言): 栈与队列的详解附完整代码
栈是一种仅允许在一端进行插入和删除操作的线性表,常用于解决括号匹配、函数调用等问题。栈分为顺序栈和链栈,顺序栈使用数组存储,链栈基于单链表实现。栈的主要操作包括初始化、销毁、入栈、出栈等。栈的应用广泛,如表达式求值、递归等场景。栈的顺序存储结构由数组和栈顶指针构成,链栈则基于单链表的头插法实现。
151 3