栈实现简单计算器(中缀表达式)

目录

一、需求一

思路分析

代码实现

二、需求二

思路分析

 代码实现

三、总结


一、需求一

    A:要求输入的是中缀表达式,如1+2*3-10/5;

    B:要求输入运算符只有加、减、乘、除;

    C:返回中缀表达式的结果;

  • 思路分析

    A:定义两个栈对象,一个用于存放表达式中的数据,一个用于存放表达式中的符号;

扫描二维码关注公众号,回复: 8835097 查看本文章

    B:中缀表达式以字符串的形式给出,利用循环对字符串进行扫描得到每一个字符;

       a:该字符为符号,则判断符号栈是否为空;

          是:直接入栈;

          否:判断当前字符的优先级与符号栈栈顶元素的优先级;

              a:若当前字符的优先级更高,那么,将该字符入栈;

              b:若当前字符的优先级低于或等于符号栈栈顶元素,那么就将从存放数据的栈中

                 弹出两个元素,弹出符号栈栈顶元素,做运算,结果在入数据栈;

       b:该字符不是符号,判断该字符是否为字符串最后一个元素;

              是:将该字符转换为Integer类型后压入堆栈;

              否:判断该字符的下一个字符是否为符号?

                   是:将该字符转换为Integer类型后压入堆栈;

                   否:不搭理

    C: 字符串扫描完毕后,利用循环,每次数栈中出栈两个元素,符号栈中出栈一个符号,

         当符号栈为空的时候,就退出循环,将数栈中的值pop出;

  • 代码实现

package cn.itcast_01;

public class Calculator {
    public static void main(String[] args) {
		//定义一个字符串表达式
    	String expression = "1-2*3+8/4";
    	
    	//创建两个栈
    	ArrayStack2 numStack = new ArrayStack2(10);
    	ArrayStack2 operStack = new ArrayStack2(10);
    	
    	//定义相关变量
	    int index = 0;
	    int num1 = 0;
	    int num2 =0;
	    int oper = 0;
	    int res = 0;
	    char ch = ' ';//保存扫描的char
	    String keepNum = "";//用于拼接多位数
	    
	    //使用循环来扫描字符串
		while (true) {
			// 获取字符
			ch = expression.substring(index, index + 1).charAt(0);
			// 判断ch是什么
			if (operStack.isOper(ch)) {
				// 判断当前符号栈是否为空
				if (!operStack.isEmpty()) {
					// 不为空
					while (!operStack.isEmpty() && operStack.priority(ch) <= operStack.priority(operStack.peek())) {
						if (numStack.Length() >= 1)
							num1 = numStack.pop();
						num2 = numStack.pop();
						oper = operStack.pop();
						res = numStack.cal(num1, num2, oper);
						// 把运算结果入数栈
						numStack.push(res);
					}
					// 当前操作符优先级大于栈内操作符优先级
					operStack.push(ch);
				} else {
					// 为空
					operStack.push(ch);
				}
			} else {
				// 1.数字是多位数的情况
				keepNum += ch;

				// 判断ch是否为字符串最后一个元素
				if (index == expression.length() - 1) {
					numStack.push(Integer.parseInt(keepNum));
				} else {
					// 判断下一个字符是不是符号
					if (operStack.isOper(expression.substring(index + 1, index + 2).charAt(0))) {
						numStack.push(Integer.parseInt(keepNum));
						// 重要的地方,清空keepNum
						keepNum = "";
					}
				}
			}
	    	index++;
	    	if(index >= expression.length()) {
	    		break;
	    	}
	    }
	    //表达式扫描完毕后,进行运算
	    while(true) {
	    	//如果符号栈为空,则计算结束,数栈中只有1个值
	    	if(operStack.isEmpty()) {
	    		break;
	    	}
	    	num1 = numStack.pop();
			num2 = numStack.pop();
			oper = operStack.pop();
			res = numStack.cal(num1, num2, oper);
			numStack.push(res);
	    }
	    System.out.printf("表达式的结果%s = %d",expression,numStack.pop());
	}
}

//先创建一个栈,需要扩展功能
class ArrayStack2 {
	// 列写栈的成员变量
	private int top = -1;
	private int[] array;
	private int maxSize;

	// 给出栈的带参构造方法
//	ArrayStack2(int[] array, int maxSize) {
//		array = new int[maxSize];
//	}
	ArrayStack2(int MaxSize) {
		this.maxSize = MaxSize;
		//初始化数组
		array = new int[this.maxSize];
	}
	//返回栈中有效元素的个数
	public int Length() {
		return top+1;
	}
	//返回当前栈顶的元素,不是出栈
	public int peek() {
		return array[top];
	}
	
	//判断栈满方法
	public boolean isFull() {
		return top == maxSize - 1;
	}
	
	//判断栈空方法
	public boolean isEmpty() {
		return top == -1;
	}

	// 定义入栈方法
	public void push(int value) {
		// 判断是否栈满
		if (isFull()) {
			System.out.println("栈满");
			return;
		} else {
			array[++top] = value;
		}
	}

	// 定义出栈方法
	public int pop() {
		// 判断是否栈空
		int value = 0;
		if (isEmpty()) {
			//抛出异常
			throw new RuntimeException("栈空");
		} else {
		    value = array[top--];
		}
		return value;
	}
	
	//栈的遍历
	public void list() {
		//判断栈是否为空
		if (isEmpty()) {
			System.out.println("栈空");
			return;
		} else {
		    for(int i=top;i>=0;i--) {
		    	System.out.print(array[i]+" ");
		    }
		}
	}
	
	//返回运算符的优先级,优先级是程序员来确定的,优先级使用数字
	//表示,数字越大,则优先级越高
	public int priority(int oper) {
		if(oper == '*'|| oper == '/') {
			return 1;
		} else if(oper == '+'|| oper == '-') {
			return 0;
		} else {
			return -1;
		}
	}
	
	//判断是不是一个运算符
	public boolean isOper(char val) {
		return val == '+'|| val == '-' || 
				val == '*' || val == '/';				
	}
	
	//运算方法
	public int cal(int num1,int num2,int oper) {
		int res = 0;
		switch(oper) {
		case '+':
			res = num1+num2;
			break;
		case '-':
			res = num2-num1;//注意顺序
			break;
		case '*':
			res = num1*num2;
			break;
		case '/':
			res = num2/num1;
			break;
		default:break;
		
		}
		return res;
	}
}

二、需求二

    A:扩展运算符,加入小括号;

  • 思路分析

    A:在上面代码的基础上加入小括号,遇到左括号要入符号栈,并且要留在符号栈中,等与右括号出现;

    B:为保证左括号留在符号栈中,其优先级就默认返回-1,即最小;

    C:遇到右括号时,就计算左右括号之间的数据,最后把结果保存到数栈,然后弹出左括号;

    D:为增强程序的可读性,将代码相同的部分定义为方法;

  • 代码实现

package cn.itcast_02;

import java.util.Stack;

/*
 * 需求:
 *    A:要求输入的是中缀表达式,如(10+2)*3-10/5-(8/4);
 *    B:要求输入运算符只有加、减、乘、除和小括号;
 *    C:返回中缀表达式的结果;
 *     
 */
public class TestDemo {
	public static void main(String[] args) {
		// 定义字符串,用于存放中缀表达式
		String expression = "(10+2)*3-10/5-(8/4)";

		// 定义两个栈对象,即数栈和符号栈
		Stack<Integer> numStack = new Stack<Integer>();
		Stack<Character> operStack = new Stack<Character>();

		// 利用循环对字符串进行扫描
		scanString(numStack, operStack, expression);

		// 表达式扫描完毕后,进行运算
		while (true) {
			// 如果符号栈为空,则计算结束,数栈中只有1个值
			if (operStack.isEmpty()) {
				break;
			}
			// 符号栈非空,则继续运算
			function(numStack, operStack);
		}
		System.out.printf("表达式的结果%s = %d", expression, numStack.pop());
	}

	// 定义字符串扫描函数
	public static void scanString(Stack<Integer> numStack, Stack<Character> operStack, String expression) {
		// 定义相关变量
		char oper = ' ';
		String keepNum = "";// 用于拼接多位数

		for (int i = 0; i < expression.length(); i++) {
			// 获取该字符串的每一个字符
			char ch = expression.substring(i, i + 1).charAt(0);
			// 判断该字符是运算符还是数字
			if (isOper(ch)) {
				// 判断符号栈是否为空
				if (operStack.isEmpty()) {
					operStack.push(ch);
				} else if (ch == '(') {
					// 符号栈不为空,ch为'('的情况
					operStack.push(ch);
				} else if (ch == ')') {
					// 符号栈不为空,ch为')'的情况
					oper = operStack.peek();
					while (oper != '(') {
						// 将栈中左括号上面的运算符弹出并运算
						function(numStack, operStack);
						oper = operStack.peek();
					}
					operStack.pop();// 弹出左括号
				} else {
					// 符号栈非空,且ch不是 '(' 或 ')' 的情况
					// 将当前ch与符号栈中运算符的优先级进行比较,可能比较多次,故选择while
					while (!operStack.isEmpty() && priority(ch) <= priority(operStack.peek())) {
						function(numStack, operStack);
					}
					operStack.push(ch);
				}
			} else {
				// 扫描到的是数字字符
				// 将ch转换为字符串,目的扫描多位数
				keepNum += ch;
				// 判断ch是否为字符串最后一个元素
				if (i == expression.length() - 1) {
					numStack.push(Integer.parseInt(keepNum));
				} else {
					// 判断下一个字符是否为数字,是就继续扫描,否则入栈
					oper = expression.substring(i + 1, i + 2).charAt(0);
					if (isOper(oper)) {
						numStack.push(Integer.parseInt(keepNum));
						keepNum = "";// 这里很重要
					}
				}
			}

		}
	}

	// 定义计算后出栈入栈功能
	public static void function(Stack<Integer> numStack, Stack<Character> operStack) {
		int num1 = 0;
		int num2 = 0;
		char oper = ' ';
		int res = 0;
		num1 = numStack.pop();
		num2 = numStack.pop();
		oper = operStack.pop();
		res = cal(num1, num2, oper);
		numStack.push(res);
	}

	// 判断字符是否为运算符
	public static boolean isOper(int ch) {
		return ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(' || ch == ')';
	}

	// 自定义运算符优先级
	public static int priority(int ch) {
		if (ch == '+' || ch == '-') {
			return 0;
		} else if (ch == '*' || ch == '/') {
			return 1;
		} else {
			return -1;// 这里左括号为保证能够在堆栈里,优先级设为最低
		}
	}

	// 定义计算函数
	public static int cal(int num1, int num2, char ch) {
		int res = 0;

		switch (ch) {
		case '+':
			res = num1 + num2;
			break;
		case '-':
			res = num2 - num1;// 这个地方就想象2-1时的情景
			break;
		case '*':
			res = num1 * num2;
			break;
		case '/':
			res = num2 / num1;
			break;
		default:
			break;
		}

		return res;
	}
}

三、总结

    A:程序的可读性比较差,只要充斥着大量if-else,调试起来就麻烦,烦人。

发布了62 篇原创文章 · 获赞 9 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Sruggle/article/details/103846316