(数据结构与算法)使用栈来实现综合计算器

1.栈实现计算器(中缀表达式)

1.1流程图

在这里插入图片描述

1.2 代码实现

public class Calculator {
    public static void main(String[] args) {
        String expression = "700*2*2-5+1-5+3-4";
        //创建数栈和运算符栈
        ArrayStackCal numStack = new ArrayStackCal(10);
        ArrayStackCal operStack = new ArrayStackCal(10);
        int index = 0;//用于扫描
        int num1 = 0;
        int num2 = 0;
        int oper = 0;
        int res = 0;
        char ch = ' '; //将扫描结果保存到ch
        String keepNum = "";//用于拼接字符串
        while (true){
            //遍历表达式将字符保存到ch
            ch = expression.substring(index,index+1).charAt(0);
            //判断是否为运算符


            if (operStack.isOper(ch)) {
                if (!operStack.isEmpty()){
                if (operStack.priority(ch) <= operStack.priority(operStack.peek())) {
                    //运算优先级小于栈顶时计算
                    num1 = numStack.pop();
                    num2 = numStack.pop();
                    oper = operStack.pop();
                    res = operStack.cal(num1, num2, oper);
                    //将运算结果入数栈
                    numStack.push(res);
                    //将当前操作符入符号栈
                    operStack.push(ch);
                } else  {
                    //运算优先级大于栈顶时入符号栈
                    operStack.push(ch);
                }
            }else {
                //如果为空直接入栈
                    operStack.push(ch);
                }

            }else {
                //如果下一位还是数字就拼接
                keepNum += ch;

                //如果ch已经是expression的最后一位,就直接入栈
                if (index == expression.length() - 1) {
                    numStack.push(Integer.parseInt(keepNum));
                }else{

                    //判断下一个字符是不是数字,如果是数字,就继续扫描,如果是运算符,则入栈
                    //注意是看后一位,不是index++
                    if (operStack.isOper(expression.substring(index+1,index+2).charAt(0))) {
                        //如果后一位是运算符,则入栈 keepNum = "1" 或者 "123"
                        numStack.push(Integer.parseInt(keepNum));
                        //重要的!!!!!!, keepNum清空
                        keepNum = "";

                    }
                }
            }
            index++;
            if (index>=expression.length()){
                break;
            }
        }
        //当表达式遍历完,就顺序的从数栈和符号栈中pop出相应的数和符号,并运行
        while (true){
            if (operStack.isEmpty()){
                break;
            }
            num1 = numStack.pop();
            num2 = numStack.pop();
            oper = operStack.pop();
            res = operStack.cal(num1,num2,oper);
            numStack.push(res);
        }
        int res2 = numStack.pop();
        System.out.println("表达式"+expression+"="+res2);
    }
}

//继承ArrayStack用数组实现栈
class ArrayStackCal extends ArrayStack{
    public ArrayStackCal() {
    }

    public ArrayStackCal(int maxSize) {
        super(maxSize);
    }

    /**
     * 返回运算符的优先级,用数字表示
     * @param oper
     * @return
     */
    public int priority(int oper){
        if (oper=='*'||oper=='/'){
            return 1;
        }else if (oper=='+' || oper=='-'){
            return 0;
        }else {
            return -1;
        }
    }

    /**
     * 判断是否是一个运算符
     * @param val
     * @return
     */
    public boolean isOper(char val){
        return val =='+' ||val =='-' || val =='*'|| val =='/';
    }

    /**
     * 返回计算结果
     * @param num1
     * @param num2
     * @param oper
     * @return
     */
    public int cal(int num1,int num2,int oper){
        int res = 0;//计算结果
        switch (oper){
            case '+':
                res = num2 + num1;
                break;
            case '-':
                res = num2 - num1;
                break;
            case '*':
                res = num2 * num1;
                break;
            case '/':
                res = num2 / num1;
                break;

            default:
                break;
        }
    return res;
    }

}

ArrayStack

class ArrayStack implements Stack1{
    public int maxSize;//栈的最大长度
    public int[] stack;
    public int top = -1;//栈顶
    public ArrayStack() {
    }

    public ArrayStack(int maxSize) {
        this.maxSize = maxSize;
        stack=new int[this.maxSize];
    }
    @Override
    public int getSize() {
        return stack.length;
    }

    @Override
    public boolean isEmpty() {
        return top==-1;
    }

    @Override
    public void push(int value) {
        if (isFull()){
            System.out.println("栈满!");
            return;
        }
        top++;
        stack[top]=value;
    }
    public boolean isFull(){
        return top==maxSize-1;
    }
    @Override
    public int pop() {
        if (isEmpty()){
            throw  new RuntimeException("栈空,无法取出数据!");
        }
        int value = stack[top];
        top--;
        return value;
    }

    @Override
    public int peek() {
        //返回栈顶的值
        return stack[top];
    }
    public void traverse(){
        if (isEmpty()){
            System.out.println("栈空,无法取出数据!");
            return;
        }
        for (int i = top; i >=0 ; i--) {
            System.out.println("stack["+i+"]"+"="+stack[i]);
        }
    }
}

stack1

public interface Stack1 {
    int getSize();
    boolean isEmpty();
    void push(int e);
    int pop();
    int peek();
}

在这里插入图片描述

2. 逆波兰(后缀)表达式实现

例如:(3+4)X5-6对应的后缀表达式就是,针对后缀表达式求值步骤如下:
1.从左至右扫描,将3和4压入堆栈;
2.遇到+运算符,因此弹出4和3 (4为栈顶元素,3为次顶元素),计算出3+4的值,得7,再将7入栈;
3.将5入栈;
4.接下来是X运算符,因此弹出5和7,计算出7X5=35,将35入栈;
5.将6入栈;
6.最后是-运算符,计算出35-6的值,即29,由此得出最终结果

2.1 初步实现

实现思路

  1. 定义表达式

  2. 将表达式按空格分割放入列表

  3. 计算结果 :从左到右扫描,扫到的是数字就入栈,符号就pop出两个数字然后进行运算,再将结果入栈循环执行。直到最后还在栈中的元素就是结果。

  4. 返回结果

//逆波兰表达式的计算
public class PolanNotation {
    public static void main(String[] args) {
        //定义逆波兰表达式
        String expression = "3 4 + 5 * 6 -";
        ArrayList<String> list = getListString(expression);
        System.out.println(list);
        int cal = cal(list);
        System.out.println(cal);
    }

    /**
     * 将表达式放入ArrayList中
     * @param expression
     * @return
     */
    public static ArrayList<String> getListString(String expression){
        //将表达式按空格分割
        String[] s = expression.split(" ");
        ArrayList<String> list = new ArrayList<>();
        //将表达式放入ArrayList中
        for (String s1 : s) {
            list.add(s1);
        }
        return list;
    }

    public static int cal(ArrayList<String> list){
        Stack<String> stack = new Stack<>();
        int num1 = 0;
        int num2 = 0;
        int res = 0;//存放运算结果
        for (String s : list) {
            //用正则表达式取出数
            if(s.matches("\\d+")){//匹配的是多位数
                stack.push(s);
            }else {
                num1 = Integer.parseInt(stack.pop());
                num2 = Integer.parseInt(stack.pop());
                switch (s){
                    case "+":
                        res = num2+num1;
                        break;
                    case "-":
                        res = num2-num1;
                        break;
                    case "*":
                        res = num2*num1;
                        break;
                    case "/":
                        res = num2/num1;
                        break;
                    default:
                        throw new RuntimeException("运算符有误");

                }
                //将计算结果入栈
                stack.push(""+res);
            }
        }
        //返回栈中最后剩下的元素
        return Integer.parseInt(stack.pop());
    }
}

在这里插入图片描述
这里表达式是后缀表达式,下面添加中缀转后缀表达式的实现。

2.2 中缀表达式转后缀表达式

2.2.1步骤

1)初始化两个栈:运算符栈s1和储存中间结果的栈s2;
2)从左至右扫描中缀表达式; .
3)遇到操作数时,将其压s2;
4)遇到运算符时,比较其与s1栈顶运算符的优先级:
1.如果s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
2.否则,若优先级比栈顶运算符的高,也将运算符压入s1;
3.否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4-1)与s1中新的栈顶运算符相比较;
5)遇到括号时:
(1) 如果是左括号“(”,则直接压入s1
(2)如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
6)重复步骤2至5,直到表达式的最右边
7)将s1中剩余的运算符依次弹出并压入s2
8)依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式

举例 1+((2+3)X4)-5

扫描到的元素s2 (栈底->栈顶)s1(栈底->栈顶)说明
11数字,入栈s2
+1+s1为空,入栈s1
(1+ (左括号,入栈s1
(1+ ( (左括号,入栈s1
21 2+ ( (数字,入栈s2
+1 2+ ( ( +s1栈顶为左括号 ,入栈s1
31 2 3+ ( ( +数字,入栈s2
)1 2 3 ++ (右括号,弹出运算符直到遇到左括号
*1 2 3 ++ ( *s1栈顶为左括号,入栈s1
41 2 3 + 4+ ( *数字,入栈s2
)1 2 3 + 4 *+右括号,弹出运算符直到遇到左括号
-1 2 3 + 4 * +--与+优先级相同,弹出+,压入-
51 2 3 + 4 * + 5-数字,入栈s2
扫描结束1 2 3 + 4 * + 5 -s1剩余运算符压入s2

2.2.2 代码实现

//逆波兰表达式的计算
public class PolanNotation {
    public static void main(String[] args) {
        String expression2 = "1+((2+3)*4)-5";
        //将中缀表达式放入集合
        List<String> list1 = toInfixExpressionList(expression2);
        System.out.println("转换前"+list1);
        //将中缀表达式转为后缀表达式
        List<String> list2 = parseSuffixExpreesionList(list1);
        System.out.println("转换后"+list2);
        int res = cal(list2);
        System.out.println(expression2+"="+res);
//        //定义逆波兰表达式
//        String expression = "3 4 + 5 * 6 -";
//        ArrayList<String> list = getListString(expression);
//        System.out.println(list);
//        int cal = cal(list);
//        System.out.println(cal);
    }

    /**
     * 将中缀表达式转为后缀表达式
     * @param list
     * @return
     */
    public static List<String> parseSuffixExpreesionList(List<String> list){
        Stack<String> s1 = new Stack<>();//存放运算符
        List<String> s2 = new ArrayList<>();//存放中间结果
        for (String s : list) {
            if (s.matches("\\d+")){
                //遇到操作数时,将其放入s2;
                s2.add(s);
            }else if(s.equals("(")) {
                //如果是左括号“(”,则直接压入s1
                s1.push(s);
            }else if(s.equals(")")){
                //)如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
                while (!s1.peek().equals("(")) {
                    s2.add(s1.pop());
                }
                s1.pop();//丢弃左括号
            }else {
                //如果是运算符,判断优先级
                while (s1.size()!=0 && Operation.getValue(s1.peek())>= Operation.getValue(s)){
                    //若当前运算符优先级小于等于栈顶元素将s1栈顶的运算符弹出并放入到s2中,再次循环判断
                    s2.add(s1.pop());
                }
                //其他情况, 如果s1为空,或栈顶运算符为左括号“(”,优先级比栈顶运算符的高,入栈
                s1.push(s);
            }
        }
        //将s1中剩余的运算符依次弹出放入s2
        while (s1.size() !=0){
            s2.add(s1.pop());
        }
        return s2;
    }
    /**
     * 将中缀表达式放入集合
     * @param s
     * @return
     */
    public static List<String> toInfixExpressionList(String s){
        List<String> list = new ArrayList<>();
        String str;//拼接字符串
        char c  ;//存放遍历的字符
        int i=0; //用于遍历字符串
        do {
            if ((c=s.charAt(i))<48||(c=s.charAt(i))>57){//当c为运算符时
                list.add(""+c);
                i++;
            }else {//当c为一个数时
                str="";//将str置空
                while (i<s.length() && (c=s.charAt(i))>=48 &&(c=s.charAt(i))<=57){
                    str+=c;
                    i++;
                }
                list.add(str);
            }
        }while (i < s.length());
        return list;
    }

    /**
     * 将后缀表达式放入ArrayList中
     * @param expression
     * @return
     */
    public static List<String> getListString(String expression){
        //将表达式按空格分割
        String[] s = expression.split(" ");
        List<String> list = new ArrayList<>();
        //将表达式放入ArrayList中
        for (String s1 : s) {
            list.add(s1);
        }
        return list;
    }

    public static int cal(List<String> list){
        Stack<String> stack = new Stack<>();
        int num1 = 0;
        int num2 = 0;
        int res = 0;//存放运算结果
        for (String s : list) {
            //用正则表达式取出数
            if(s.matches("\\d+")){//匹配的是多位数
                stack.push(s);
            }else {
                num1 = Integer.parseInt(stack.pop());
                num2 = Integer.parseInt(stack.pop());
                switch (s){
                    case "+":
                        res = num2+num1;
                        break;
                    case "-":
                        res = num2-num1;
                        break;
                    case "*":
                        res = num2*num1;
                        break;
                    case "/":
                        res = num2/num1;
                        break;
                    default:
                        throw new RuntimeException("运算符有误");

                }
                //将计算结果入栈
                stack.push(""+res);
            }
        }
        //返回栈中最后剩下的元素
        return Integer.parseInt(stack.pop());
    }
}
//编写一个类 Operation 可以返回一个运算符 对应的优先级
class Operation {
    private static int ADD = 1;
    private static int SUB = 1;
    private static int MUL = 2;
    private static int DIV = 2;

    //写一个方法,返回对应的优先级数字
    public static int getValue(String operation) {
        int result = 0;
        switch (operation) {
            case "+":
                result = ADD;
                break;
            case "-":
                result = SUB;
                break;
            case "*":
                result = MUL;
                break;
            case "/":
                result = DIV;
                break;
            default:
                break;
        }
        return result;
    }

}

在这里插入图片描述

武汉理工大学的数据结构与算法实验包括线性表的应用、栈的应用、树的应用、无向图的应用、有向图的应用、查找与排序、哈夫曼编码等内容。这些实验旨在帮助学生掌握数据结构的设计和应用,包括图的存储结构和创建方法、图的遍历方法、迪杰斯特拉算法、最小生成树的概念、普里姆算法等。此外,还有一个名为"简易计算器程序"的实验项目,项目内容是编写程序来模拟简单运算器的工作,实现对中缀表达式的计算。希望通过这些实验,学生能够加深对数据结构算法的理解,并提升编程能力。123 #### 引用[.reference_title] - *1* *3* [武汉理工大学数据结构课内实验](https://blog.csdn.net/mo_zhe/article/details/112733427)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}} ] [.reference_item] - *2* [武汉理工大学数据结构综合实验——图与景区信息管理系统实践](https://blog.csdn.net/mo_zhe/article/details/112771599)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值