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

栈Stack(数组模拟、单链表模拟)

栈是一个先入后出的有序列表,是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。允许插入和删除的一端,为变化的一端,称为栈顶定(top),另一端为固定的一端,称为栈底(bottom)。根据栈的定义可知,最先放入栈中元素在栈底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除。

入栈图解:

出栈图解:

应用场景:

1)子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完毕之后再将地址取出,以回到原来的程序中。

2)处理递归调用:和子程序的调用类似,只是除了储存下一指令的地址外,也将参数、区域变量等数据存入堆栈中。

3)表达式的转换[中缀表达式-->后缀表达式]与求值(实际解决)。

4)二叉树的遍历。

5)图形的深度优先搜索法。

 

案例:

1.用数组模拟栈的使用

思路分析:

定义一个变量指针top,初始化top = -1,始终指向栈顶元素。

入栈操作push:当有数据加入到栈时,top++;stack[top];

出栈操作pop:从栈顶取出数据并返回,int value = stack[top];top--;return value;

public class Stack {
    public int maxSize;
    public int top = -1;
    public int[] stack; // 成员变量会初始化

    // 构造器(记牢这个写法)
    public Stack(int maxSize) {
       this.maxSize = maxSize;
       stack = new int[maxSize];
    }

    // 判栈满
    public boolean isFull(){
        /*if(top == maxSize - 1){
            return true;
        }else return false;*/

        return top == maxSize;
    }

    // 判栈空
    public boolean isEmpty(){
        return top == -1;
    }

    // 加元素于栈顶
    public void push(int value){
        // 判栈满
        if(isFull()){
            System.out.println("栈满,无法push!");
            return; // 直接结束方法
        }
        stack[++top] = value;
    }

    // 从栈顶弹出元素
    public int pop(){
        // 判栈空
        if(isEmpty()){
           // System.out.println("栈空,无法pop!");
            // 抛出异常处理
            // throw关键字用在方法内,用来抛出一个异常对象
            // 将这个异常对象传递到调用者处,并结束当前方法的执行。
            // 无需再声明异常,默认交给JVM处理(打印异常对象,中断程序)
            throw new RuntimeException("栈空,无法pop!"); // 同样跳出方法
        }
        return stack[top--];
    }

    // 遍历栈,从栈顶往下遍历
    public void show(){
        if(isEmpty()){
            System.out.println("栈空,无法遍历");
            return; // 结束方法
        }
        for(int i = top;i > -1;i--){
            System.out.println(stack[i]);
        }
    }
}

2.用单链表模拟栈

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
 */

public class LinkedListStack {
    public int maxSize; // 容量
    public int top = -1; // 栈顶指针
    public ListNode listNode; // 链表

    // 初始化
    public LinkedListStack(int maxSize) {
        this.maxSize = maxSize;
    }

     // 判空
    public boolean isEmpty(){
        return listNode == null;
    }

    // 判满,链表元素个数和maxSize相同
    public boolean isFull(){
        int count = 0;
        ListNode node = listNode; // 遍历指针
        while(node != null){
            count ++;
            node = node.next;
        }
        return count == maxSize;
    }

    // 往栈顶添加元素
    public void push(int value){
        // 判满
        if(isFull()){
            System.out.println("栈满,无法插入元素!");
            return;
        }
        // 头插法
        if(isEmpty()) listNode = new ListNode(value); // 第一个元素
        else { // 其他元素
            ListNode node = new ListNode(value); // 默认指针域null
            node.next = listNode;
            listNode = node;
        }
    }

    // 弹出栈顶元素
    public int pop(){
        // 判空
        if(isEmpty()){
            throw new NullPointerException("栈空,无法弹出元素"); // 异常,直接终止
        }
        int temp = listNode.val;
        listNode = listNode.next;
        return temp;
    }

    // 遍历(与输入顺序相反,刚好满足头插,所以直接顺序遍历即可)
    public void show(){
        // 判空
        if(isEmpty()){
            System.out.println("栈空,无法遍历");
            return; // 退出方法
        }
        ListNode node = listNode; // 遍历指针
        while(node != null){
            System.out.println(node.val);
            node = node.next;
        }
    }
}

3.使用栈来实现综合计算器--中缀问题

两个栈:一个专门存放数,一个专门存放运算符。

步骤:

1.通过index


扫遍历字符串表达式;

2.当扫描到的是数字,直接入数栈;

3.当扫描的是运算符,分情况入栈。

4.如果当前符号栈为空,该运算符直接入栈;如果当前符号栈不为空(有运算符),就对栈顶元素运算符与该运算符进行优先级比较(小于等于,就从数栈pop出两个数,从符号栈pop出一个运算符,进行运算,将得到的结果再入数栈,该运算符也直接入栈,(注意此处并非继续与当前符号栈下一个栈顶元素比较,直到优先级大于栈顶元素或栈空入栈,因为这样就增加了遍历的时间复杂度)

5.当表达式扫描完毕,就顺序地从数栈和符号栈中pop出相应的数和运算符,并运算。

6.最后在数栈只有一个数字,就是表达式的结果。

案例实现(3+2*6-2)

Stack numberStack = new Stack(10);
Stack operStack = new Stack(10);
String str = "3+2*6-2";
int num1 = 0;
int num2 = 0;
int oper = 0;
char[] chars = str.toCharArray();
// 转为数组,以便遍历
for (char val:chars) {
    // 判断val是什么
    if(operStack.isOper(val)){ // 如果是运算符
        // 判断当前符号栈是否为空
        if(operStack.isEmpty()){ // 直接入栈
            operStack.push(val);
        }else{ // 不为空
            char top = (char) operStack.stack[operStack.top]; // 栈顶值
            // 判断栈顶元素和当前元素的优先级
            if(operStack.priority(val) <= operStack.priority(top)){ // 自动转换
                // 当前元素优先级优先级小于等于栈顶元素优先级,则取出来运算
                num1 = numberStack.pop(); // 先
                num2 = numberStack.pop(); // 后
                oper = operStack.pop(); // 运算符
                numberStack.push(numberStack.cal(num1,num2,oper)); // 运算
                operStack.push(val);
            }else{ // 当前运算符优先级大于栈顶元素优先级
                operStack.push(val);
            }
        }
    }else{ // 是数,直接入数栈
        numberStack.push(val - '0');
    }
}

// 扫描完毕后,相应pop运算到数栈只剩结果
while(operStack.isEmpty()){ // 符号栈为空,数栈只剩一个结果
    num1 = numberStack.pop(); // 先
    num2 = numberStack.pop(); // 后
    oper = operStack.pop(); // 运算符
    numberStack.push(numberStack.cal(num1,num2,oper)); // 运算
}

// 打印结果
System.out.print("表达式" + str);
System.out.println("运算结果为:" + numberStack.pop());

 

 

相关文章:

  • 属性集合Properties
  • 缓冲流
  • 转换流InputStreamReader类和OutputStreamWriter(字符编码和字符集)
  • 序列化与反序列化和transient瞬态关键字
  • 打印流
  • 前缀(波兰)、中缀、后缀(逆波兰)表达式
  • 递归(回溯之迷宫问题+八皇后)
  • 算法题-Java实现:从 1 到 n 整数中 1 出现的次数(时间复杂度O(logn))
  • 软件结构/网络通信协议/IP地址/端口号
  • TCP通信程序
  • C/S结构文件上传案例
  • 排序算法(冒泡排序、选择排序、插入排序、希尔排序、快速排序、归并排序、基数排序)
  • 二分查找和插值查找
  • 树-二叉树(前中后序遍历+按值查找+删除节点)
  • 位运算应用场景(每遇更新)
  • 网络传输文件的问题
  • C++类中的特殊成员函数
  • Flex布局到底解决了什么问题
  • Go 语言编译器的 //go: 详解
  • gulp 教程
  • JavaScript类型识别
  • JWT究竟是什么呢?
  • Less 日常用法
  • Nodejs和JavaWeb协助开发
  • React-生命周期杂记
  • Vue实战(四)登录/注册页的实现
  • 反思总结然后整装待发
  • 观察者模式实现非直接耦合
  • 浅谈web中前端模板引擎的使用
  • 使用 5W1H 写出高可读的 Git Commit Message
  • 视频flv转mp4最快的几种方法(就是不用格式工厂)
  • 小程序、APP Store 需要的 SSL 证书是个什么东西?
  • 一起参Ember.js讨论、问答社区。
  • 用jquery写贪吃蛇
  • 智能网联汽车信息安全
  • 【运维趟坑回忆录 开篇】初入初创, 一脸懵
  • linux 淘宝开源监控工具tsar
  • ​七周四次课(5月9日)iptables filter表案例、iptables nat表应用
  • ​香农与信息论三大定律
  • #LLM入门|Prompt#1.7_文本拓展_Expanding
  • (22)C#传智:复习,多态虚方法抽象类接口,静态类,String与StringBuilder,集合泛型List与Dictionary,文件类,结构与类的区别
  • (C语言)strcpy与strcpy详解,与模拟实现
  • (DenseNet)Densely Connected Convolutional Networks--Gao Huang
  • (NSDate) 时间 (time )比较
  • (二)斐波那契Fabonacci函数
  • (分布式缓存)Redis分片集群
  • (简单) HDU 2612 Find a way,BFS。
  • (力扣记录)235. 二叉搜索树的最近公共祖先
  • (六)激光线扫描-三维重建
  • (四)鸿鹄云架构一服务注册中心
  • (学习日记)2024.01.19
  • (一)ClickHouse 中的 `MaterializedMySQL` 数据库引擎的使用方法、设置、特性和限制。
  • (一)VirtualBox安装增强功能
  • (已解决)什么是vue导航守卫
  • ***原理与防范