基本思路:

(1)通过一个index值作为索引(int类型),来遍历多项式;

(2)如果扫描到一个数字,就直接入数栈;

(3)如果扫描到一个符号,就分以下情况:

a、如果当前符号栈为空,就将该符号直接入符号栈;

b、如果符号栈不为空,就要进行优先级的比较:

    1、如果 当前扫描到的操作符 的优先级 小于或等于 栈顶的操作符,就要从 数栈 中 pop 出两个数,同时从 符号栈 中 pop 出一个符号,构成运算式进行运算。运算得到的结果 入数栈,然后再将 当前扫描到的操作符 入符号栈

    2、如果 当前扫描到的操作符 的优先级 大于 栈顶的操作符,就直接入符号栈。

(4)当多项式扫描完毕时,就依次从 数栈 符号栈 中 pop 出相应的数和符号(每一轮pop出 2个数和1个符号 ,组成一个运算式进行运算,将运算结果 压入数栈),循环往复。

(5)最后,符号栈会空,数栈中留下最后一个数,该数即为运算结果


首先创建一个数组栈,实现基本的操作如下:

// 定义一个ArrayStack 表示栈
class ArrayStackCul {
	private int maxSize; // 栈的最大容量
	private int[] stack; // 数组模拟栈,数据放在该数组中
	private int top = -1;//栈顶指针

	// 构造器
	public ArrayStackCul(int maxSize) {
		this.maxSize = maxSize;
		stack = new int[this.maxSize];
	}

	// 判断栈满
	public boolean isFull() {
		return top == maxSize - 1;
	}

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

	// 入栈
	public void push(int value) {
		// 入栈前先判断栈是否满
		if (isFull()) {
			System.out.println("栈满!");
			return;
		}
		top++;
		stack[top] = value;
	}

	// 出栈
	public int pop() {
		// 出栈前,先判断栈是否空
		if (isEmpty()) {
			// 由于必须要返回值,所以用抛出异常来处理
			throw new RuntimeException("栈空,没有数据~");
		}
		int value = stack[top];
		top--;
		return value;
	}

	// 栈的遍历(只能从栈顶往下遍历)
	public void list() {
		if (isEmpty()) {
			System.out.println("没有数据,无法遍历");
			return;
		}
		for (int i = top; i >= 0; i--) {
			System.out.printf("stack[%d] = %d\n", i, stack[i]);
		}
	}
}

为了实现多项式运算,还要为该栈功能进行拓展:

(1)为了比较扫描串中 当前扫描的运算符栈顶运算符 的优先级,定义peek()方法,直接返回 栈顶的元素值,但不取出;

(2)比较运算符优先级的前提是定义和返回优先级,故定义priority(char oper)方法,用于返回操作符oper的优先级;

(3)因为多项式中既有操作符又有数字,为了方便区分,定义isOper(char)方法,用于判断该符号是否是操作符

(4)定义一个cal(int num1,  int num2 , char oper)方法,根据oper运算符的不同,实现不同运算式的统一计算

// 为了实现运算器的功能,需拓展功能:
	/*
	 * 返回运算符的优先级,优先级由程序员来确定,用数字来表示 默认:数字越大,优先级越高
	 */
  
  	/*
	 * 为了将扫描串中的运算符与栈顶运算符进行比较 定义一个方法,直接返回栈顶的元素值,但不取出
	 */
    //该方法用于查看栈顶元素
	public int peek() {
		return stack[top];
	}
  
    // 该方法用于返回元素的优先级
	// 在java中int 和 char 是可以比较的
	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 == '/';
	}// 在当前情景下,不是运算符当然就只能是数字了


    //此方法用于计算出栈元素构成运算式的运算结果
	// num1比num2先出栈,oper为运算符
	public int cal(int num1, int num2, int oper) {
		int res = 0;// res用来存放运算的结果
		switch (oper) {
		case '+':
			res = num2 + num1;
			break;
		case '-':
			res = num2 - num1;
			break;
		case '*':
			res = num2 * num1;
			break;
		case '/':
			res = num2 / num1;
			break;
		default:
			System.out.println("符号有误。");
			break;
		}
		return res;
	}

最后在main函数中实现开头的思路:(难点)注释详细

package Stack;

public class Calculator {
	public static void main(String[] args) {
		// 根据前面的思路,完成表达式的运算
		String expression = "70+20*6-20/4"; // 如何处理多位数的问题?
		// 创建两个栈,一个数栈,一个符号栈
		ArrayStackCul numStack = new ArrayStackCul(10);
		ArrayStackCul operStack = new ArrayStackCul(10);
		// 创建需要的变量
		int index = 0;// 用于扫描
		char ch = ' ';// 扫描到的结果存储在ch里面,进行下一步的判断

		int num1;
		int num2;
		int oper;// num1为先出栈数据
		int res;// 运算结果

		String keepNum = "";// 用于处理多位数运算问题(难点),该变量用来存储一个多位数
		while (true) {
			// 开始扫描
			ch = expression.substring(index, index + 1).charAt(0);
			// subString指的是取一个子串;charAt(0)表示取子串的第一个字符
			if (operStack.isOper(ch)) {// 如果是运算符
				if (!operStack.isEmpty()) {
					if (operStack.priority(ch) <= operStack.priority(operStack.peek())) {
						// ① 扫描的当前运算符 < 栈顶运算符 ——> 执行思路1
						num1 = numStack.pop();
						num2 = numStack.pop();
						oper = operStack.pop();
						res = numStack.cal(num1, num2, oper);
						// 运算出的结果入栈
						numStack.push(res);
						// 最后还要将当前优先级小的运算符入栈
						operStack.push(ch);
					} else {
						// ② 当前运算符的优先级大,执行思路2
						operStack.push(ch);
					}
				} else {// 如果当前符号栈为空,则直接入栈
					operStack.push(ch);
				}
			} else {// 如果是数字,直接入数字栈
				/*
				 * // numStack.push(ch);//这样写是错误的,因为我们得到的数字其实是'字符'
				 * numStack.push(ch - 48);//字符模式的数 与 数字模式的数 ASCII码差了48
				 */
				// 以上做法无法处理多位数的问题,解决多位数计算问题的思路:
				/*
				 * 1、当处理数时,不能发现是一个数,就直接入栈 2、在处理数字时,需要想expression的表达式的index后再看一位
				 * 如果是数,就继续扫描;如果是运算符,则将扫描的整体转换成数值后入栈
				 * 3、因此需要定义一个字符串变量用于拼接(KeepNum)
				 */
				keepNum += ch;
				// 往后试探一位index + 1 ,如果是数字,则继续扫描;如果是运算符,则入栈
				// 另外如果ch已经是expression的最后一位,就直接入栈
				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 + 1 , 并判断是否扫描到expression的最后
			// index只是扫描的索引,每扫描一个字符就+1,index >= 字符串长度时,表示扫描完
			index++;
			if (index >= expression.length()) {
				break;
			}
		}

		// 表达式扫描完毕后,
		// 依次弹出两个栈中的数和符号,依次计算结果,直到符号栈空
		while (true) {
			// ③ 如果符号栈为空,则计算最后的结果,即数栈中的最后一个数
			if (operStack.isEmpty()) {
				break;
			}
			num1 = numStack.pop();
			num2 = numStack.pop();
			oper = operStack.pop();
			res = numStack.cal(num1, num2, oper);
			numStack.push(res);
		}
		// 将数栈中最后一个数pop出来,就是结果
		int result = numStack.pop();
		System.out.printf("表达式%s = %d", expression, result);

	}

}
}