栈 之 如何实现一个栈

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

前言

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

栈的操作

栈是一种先进后出(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)、中缀表达式转后缀表达式

笔记

相关文章
|
1月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
159 9
|
1月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
29 1
|
19天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
42 5
|
1月前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
1月前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
1月前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
51 4
|
2月前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
43 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
1月前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
26天前
|
算法
数据结构之购物车系统(链表和栈)
本文介绍了基于链表和栈的购物车系统的设计与实现。该系统通过命令行界面提供商品管理、购物车查看、结算等功能,支持用户便捷地管理购物清单。核心代码定义了商品、购物车商品节点和购物车的数据结构,并实现了添加、删除商品、查看购物车内容及结算等操作。算法分析显示,系统在处理小规模购物车时表现良好,但在大规模购物车操作下可能存在性能瓶颈。
42 0
|
2月前
初步认识栈和队列
初步认识栈和队列
61 10