栈模型(LIFO)

限制插入和删除只能在一个位置进行的表,即栈顶操作。

基本操作

  • push()
  • pop()
  • top()

栈的实现

ArrayList和LinkedList都支持栈操作。可以使用链式结构或者使用数组。

栈的应用

  • 平衡符号
package top.carrotguo.stack.apply;

import com.sun.org.apache.xerces.internal.impl.xpath.regex.Match;
import top.carrotguo.bean.stack.Stack;

/**
 * 括号匹配
 * @Author: Carrot
 * @Mail: 1053155183carrot@gmail.com
 * @Date: Created in 13:14 2018-06-10
 */
public class Matching {

    //表达式
    private String expression;
    private String e;
    //简化后只有括号的表达式
    private String simple="";
    private Stack<Character> stack;

    public Matching(String expression){
        this.expression = expression;
        stack = new Stack<>();
    }

    /**
     * 简化表达式只保存括号
     */
    private void simply(){
        for (int i=0; i<expression.length(); i++) {
            if (expression.charAt(i)=='('
                    || expression.charAt(i)==')'
                    || expression.charAt(i)=='['
                    || expression.charAt(i)==']'
                    || expression.charAt(i)=='{'
                    || expression.charAt(i)=='}') {
                simple += expression.charAt(i);
            }
        }
        System.out.println(simple);
    }

    /**
     * 括号匹配
     */
    public boolean isMatch(){
        //简化表达式(留括号)
        simply();
        for (int i=0; i<simple.length(); i++) {
            char e = simple.charAt(i);
            if (e == '(' || e == '[' || e == '{') {
                //左括号进栈
                stack.push(e);
            } else if (!stack.isEmpty()) {
                //右括号且栈非空  出栈一个左括号
                if (e==')'&&stack.top()=='(') {
                    stack.pop();
                } else if (e==']' && stack.top()=='[') {
                    stack.pop();
                } else if (e=='}' && stack.top()=='{') {
                    stack.pop();
                }
            } else {
                //右括号还存在 但栈已空 无法匹配
                return false;
            }
        }
        return stack.isEmpty();
    }

    public static void main(String[] args){
        Matching matching = new Matching("{[(3+1)*(3+6)]+(3-5)}+(3+5)");
        boolean isMatch = matching.isMatch();
        if (isMatch) {
            System.out.println("匹配成功");
        } else {
            System.out.println("匹配失败");
        }
    }


}

  • 进制转换
package top.carrotguo.stack.apply;

import top.carrotguo.bean.stack.Stack;

/**
 * 进制转换
 * @Author: Carrot
 * @Mail: 1053155183carrot@gmail.com
 * @Date: Created in 22:09 2018-06-09
 */
public class Conversion {
    //进制对应数字的表示形式(如十六进制的10写为A)
    private String[] digit = {"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"};
    //存储余数的栈
    private Stack<String> remainder = new Stack();

    /**
     * 进制转换
     * @param n
     * @param base  base进制
     */
    public void convert(int n,int base){
        String result = null;
        while (n>0) {
            //余数进栈
            remainder.push(digit[n%base]);
            n /= base;
        }
        System.out.print(n+"进行"+base+"进制转换 >>> ");
        while ((result=remainder.pop()) != null) {
            System.out.print(result+" ");
        }
    }

    public static void main(String[] args){
        Conversion conversion = new Conversion();
        conversion.convert(257,16);
    }

}

  • 后缀表达式(逆波兰式)求值
package top.carrotguo.leetcode.stack;

import java.util.Stack;

/**
 * 后缀表达式求值
 * @Author: Carrot
 * @Mail: 1053155183carrot@gmail.com
 * @Date: Created in 20:52 2018-07-18
 */
public class Solution {
    private Stack<Integer> numStack = new Stack();
    private Stack<Character> optrStack = new Stack();

    public int evalRPN(String[] tokens) {
        for(String str : tokens){
            if(!str.equals("+")&&!str.equals("-")&&!str.equals("*")&&!str.equals("/")){
                numStack.push(Integer.valueOf(str));
            } else {
                switchCalu(str);
            }
        }
        return numStack.pop();
    }

    private void switchCalu(String op){
        int num2 = numStack.pop();
        int num1 = numStack.pop();
        int result = 0;
        switch(op){
            case "+":{
                result = num1+num2;
                break;
            }
            case "-":{
                result = num1-num2;
                break;
            }
            case "*":{
                result = num1*num2;
                break;
            }
            case "/":{
                result = num1/num2;
                break;
            }
        }
        numStack.push(result);
    }

    public static void main(String[] args){
        Solution solution = new Solution();
        int result1 = solution.evalRPN(new String[]{"2","1","+","3","*"});
        int result2 = solution.evalRPN(new String[]{"4","13","5","/","+"});
        System.out.println(result1);
        System.out.println(result2);
    }
}

  • 中缀表达式转后缀表达式
package top.carrotguo.stack.apply;

import top.carrotguo.bean.stack.Stack;

import java.util.regex.Pattern;

/**
 * 中缀表达式转后缀表达式
 * @Author: Carrot
 * @Mail: 1053155183carrot@gmail.com
 * @Date: Created in 21:08 2018-06-13
 */
public class InfixToPostfix {

    private String iExpression;      //中缀表达式
    private Stack<Float> opnd;   //数字栈
    private Stack<Character> optr;   //符号栈
    private String pExpression = "";      //后缀表达式
    //优先级表
    private char[][] pri = {/*----------------当前运算符----------------------*/
                            /*+   -   *   /   ^   !   (   )   \0*/
                            /*-- + */ {'>','>','<','<','<','<','<','>','>'}
                            /*|  - */,{'>','>','<','<','<','<','<','>','>'}
                            /*栈 * */,{'>','>','>','>','<','<','<','>','>'}
                            /*顶 / */,{'>','>','>','>','<','<','<','>','>'}
                            /*运 ^ */,{'>','>','>','>','>','<','<','>','>'}
                            /*算 ! */,{'>','>','>','>','>','>',' ','>','>'}
                            /*符 ( */,{'<','<','<','<','<','<','<','=',' '}
                            /*|  ) */,{' ',' ',' ',' ',' ',' ',' ',' ',' '}
                            /*--\0 */,{'<','<','<','<','<','<','<',' ','='}};

    /**
     * @param express   中缀表达式
     */
    public InfixToPostfix(String express){
        iExpression = express+'\0';
        //初始化栈
        opnd = new Stack<>();
        optr = new Stack<>();
    }

    /**
     * 中缀表达式转后缀表达式
     * @return 后缀表达式
     */
    public String conver(){
        optr.push('\0');        //表达式起始符号进栈,用于控制所有符号出栈并且判断是否扫描完整个表达式
        int index = 0;            //当前扫描表达式的字符的索引
        char e;                   //当前扫描表达式的字符

        //中缀表达式转后缀表达式
        while (!optr.isEmpty()) {
            e = iExpression.charAt(index);
            //数字则读取数字并且追加到后缀表达式
            if (isNum(e)) {
                //读取数字  扫描的位置定位到下一个符号位置
                index = readNum(index)+1;
                pExpression += opnd.top()+" ";
            } else {
                //操作符处理
                //1 根据优先级处理
                //2 将index定位 进行下一轮循环
                index = switchOperate(optr.top(),index);
            }
        }
        return pExpression;
    }

    /**
     * 根据优先级处理符号
     * @param top   栈顶符号
     * @param index 当前扫描的符号
     * @return      下一次循环应该扫描的位置
     */
    private int switchOperate(char top, int index){
        char e = iExpression.charAt(index);
        char priority = orderBetween(top,e);        //获取优先级比较
        //根据优先级不同进行处理
        switch (priority) {
            case '<' : {
                //栈顶元素优先级低于当前扫描符-->直接进栈,下一次扫描位置后移一位
                optr.push(e);
                index++;
                break;
            }
            case '=' : {
                //')'或者'\0'情况-->只需栈顶弹栈,下一次扫描位置后移一位
                optr.pop();
                index++;
                break;
            }
            case '>' : {
                //栈顶元素弹栈,追加到后缀表达式中,下一次扫描位置不变
                pExpression += optr.pop()+" ";
                break;
            }
        }
        return index;
    }

    /**
     * 返回优先级比较
     * @param top   栈顶符号
     * @param e     当前扫描符号
     * @return      > < =
     */
    private char orderBetween(char top, char e){
        String digit = "+-*/^!()\0";
        int topIndex = digit.indexOf(top);
        int eIndex = digit.indexOf(e);
        return pri[topIndex][eIndex];
    }

    /**
     * 判断是否是数字
     * @param c
     * @return
     */
    private boolean isNum(char c){
        String num = "0123456789";
        if (num.indexOf(c) != -1) {
            return true;
        }
        return false;
    }

    private boolean isNum(String str){
        Pattern pattern = Pattern.compile("[0-9]+([.]{1}[0-9]+){0,1}");
        return pattern.matcher(str).matches();
    }

    /**
     * 数字转换
     * @param index
     * @return  返回连续的最后一位数字
     */
    private int readNum(int index){
        float num = Float.parseFloat(String.valueOf(iExpression.charAt(index)));

        //判断下一位是否为数字
        if (isNum(iExpression.charAt(index+1))) {
            //index后移一位
            index++;
            num = num*10+Float.parseFloat(String.valueOf(iExpression.charAt(index+1)));
        }
        //数字进栈
        opnd.push(num);
        return index;
    }

    /**
     * 根据后缀表达式计算
     * @return  表达式结果
     */
    public float calculate(String[] postFix){
        float sum = 0;
        //先清空数字栈
        while (!opnd.isEmpty()) {
            opnd.pop();
        }
        for (String str : postFix) {
            if (isNum(str)) {
                //数字进栈
                opnd.push(Float.valueOf(String.valueOf(str)));
            } else {
                //直接计算
                if (str == "!") {
                    //单操作数运算
                    float num = opnd.pop();
                    num = switchPower(num);
                    //计算结果重新入栈
                    opnd.push(num);
                } else {
                    //双操作数运算
                    float num2 = opnd.pop();
                    float num1 = opnd.pop();
                    char op = str.charAt(0);        //此时str必定是操作符  取第一个元素转化为char即为操作符
                    float result = switchCalcu(num1,num2,op);
                    //计算结果入栈
                    opnd.push(result);
                }
            }
        }
        sum = opnd.top();
        return sum;
    }

    /**
     * 根据运算符计算(二元)
     * @return
     */
    public float switchCalcu(float num1,float num2, char operator){
        float result = 0;
        switch (operator) {
            case '+' : {
                result = num1+num2;
                break;
            }
            case '-' : {
                result = num1 - num2;
                break;
            }
            case '*' : {
                result = num1 * num2;
                break;
            }
            case '/' : {
                result = num1 / num2;
                break;
            }
            case '^' : {
                result = (float) Math.pow(num1,num2);
                break;
            }
        }
        return result;
    }

    /**
     * 根据运算符计算(一元)
     * @return
     */
    public float switchPower(float num){
        float sum = 1;
        for (int i=1; i<=num; i++) {
            sum *= i;
        }
        return sum;
    }

    public static void main(String[] args){
        InfixToPostfix infixToPostfix = new InfixToPostfix("(1+2)+5^2+(6-1)");
        //System.out.println(infixToPostfix.conver());
        String[] str = infixToPostfix.conver().split(" ");
        for (String s: str) {
            System.out.print(s+" ");
        }
        float result = infixToPostfix.calculate(str);
        System.out.print("="+result);
    }

}

  • 中缀表达式求值
package top.carrotguo.stack.apply;

import top.carrotguo.bean.stack.Stack;

/**
 * 栈应用——中缀表达式求值
 * @Author: Carrot
 * @Mail: 1053155183carrot@gmail.com
 * @Date: Created in 17:14 2018-06-10
 */
public class InfixExpression {
    private char[][] pri = {            /*当前运算符*/
                            /*+   -   *   /   ^   !   (   )   \0*/
                  /*-- + */ {'>','>','<','<','<','<','<','>','>'}
                  /*|  - */,{'>','>','<','<','<','<','<','>','>'}
                  /*栈 * */,{'>','>','>','>','<','<','<','>','>'}
                  /*顶 / */,{'>','>','>','>','<','<','<','>','>'}
                  /*运 ^ */,{'>','>','>','>','>','<','<','>','>'}
                  /*算 ! */,{'>','>','>','>','>','>',' ','>','>'}
                  /*符 ( */,{'<','<','<','<','<','<','<','=',' '}
                  /*|  ) */,{' ',' ',' ',' ',' ',' ',' ',' ',' '}
                  /*--\0 */,{'<','<','<','<','<','<','<',' ','='}};
    private Stack<Character> optr;      //运算符栈
    private Stack<Float> opnd;          //运算数栈
    private String expression;          //表达式

    public InfixExpression(String expression){
        optr = new Stack<>();
        opnd = new Stack<>();
        //末尾加上'\0',  '\0' 用于控制所有操作符出栈
        this.expression = expression+'\0';
    }

    /**
     * 中缀表达式结算——栈的应用
     * @return
     */
    public float evaluate(){
        //先将表达式开始标识符'\0'进栈
        optr.push('\0');
        int i=0;        //记录当前扫描到的字符
        //扫描表达式
        while (!optr.isEmpty()) {
            //是否为数字
            if (isDigit(expression.charAt(i))) {
                //数字进栈(多位数需要进行处理为一个数字再入栈)
                i = readNum(i);
                i++;        //扫描下一位
            } else {   //当前扫描到的为运算符
                //根据优先级处理(scan为扫描过且已进栈的符号个数)
                char top = optr.top();
                int scan = switchOperate(top,expression.charAt(i));
                i += scan;
            }
        }
        return opnd.top();
    }

    /**
     * 根据优先级计算
     * @param top  操作符栈顶元素
     * @param e    当前扫描到的操作符
     */
    private int switchOperate(char top,char e){
        int scan = 0;   //进栈操作符个数
        char priority = orderBetween(top,e);
        switch (priority) {
            case '<' : {
                //栈顶优先级<当前扫描符号--操作符进栈
                optr.push(e);
                scan = 1;
                break;
            }
            case '=' : {
                //栈顶优先级=当前扫描符号--操作符弹栈,扫描表达式下一位(有括号或者\0)
                optr.pop();
                scan = 1;
                break;
            }
            case '>' : {
                char op = optr.pop();   //弹出栈顶操作符
                //单目操作符
                if (op == '!') {
                    opnd.push(calcu(op,opnd.pop()));
                } else {
                    //双目操作符
                    float num2 = opnd.pop();
                    float num1 = opnd.pop();
                    opnd.push(calcu(num1,num2,op));
                }
                scan = 0;
                break;
            }
        }
        return scan;
    }

    /**
     * 单目运算
     * @param op
     * @param num
     * @return  运算结果
     */
    private float calcu(char op, float num){
        float sum = 1;
        if (op == '!') {
            for (int i=1; i<=num; i++) {
                sum *= i;
            }
        }
        return sum;
    }

    /**
     * 双目运算
     * @param num1  第一个操作数
     * @param num2  第二个操作数
     * @param op    操作符
     * @return      结果
     */
    private float calcu(float num1, float num2, char op){
        float result = 0;
        switch (op) {
            case '+' : {
                result = num1 + num2;
                break;
            }
            case '-' : {
                result = num1 - num2;
                break;
            }
            case '*' : {
                result = num1 * num2;
                break;
            }
            case '/' : {
                result = num1 / num2;
                break;
            }
            case '^' : {
                result = (float) Math.pow(num1,num2);
            }
        }
        return result;
    }

    /**
     * 优先级比较(栈顶?当前扫描运算符)
     * @return >  <  =
     */
    private char orderBetween(char top, char e){
        String digit = "+-*/^!()\0";
        int topIndex = digit.indexOf(top);
        int scanDidex = digit.indexOf(e);
        return pri[topIndex][scanDidex];
    }

    /**
     * 判断字符是否为数字
     * @param e
     * @return
     */
    private boolean isDigit(char e){
        String num = "0123456789";
        if (num.indexOf(e)!=-1) {
            return true;
        }
        return false;
    }

    /**
     * 数字转化并进栈(因为多位数是多个字符组成  需要转化为float)
     * @param i   当前扫描到表达式的位置
     * @return    扫描到的最后一个连续数字的位置
     */
    private int readNum(int i){
        float num = Float.parseFloat(String.valueOf(expression.charAt(i)));
        //判断表达式下一位是否是数字,是数字则要进行转换
        while (isDigit(expression.charAt(i+1))) {
            i++;
            num = num*10+Float.parseFloat(String.valueOf(expression.charAt(i)));
        }
        //数字进栈
        opnd.push(num);
        return i;
    }

    public static void main(String[] args){
        InfixExpression infixExpression = new InfixExpression("(1+2)+5^2+(6-1)");
        float result = infixExpression.evaluate();
        System.out.println("计算结果 >> "+result);
    }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容