数据结构【栈】栈的基本概述以及综合计算器的实现

1 栈的概述以及基本代码实现

栈(Stack)是一种先入后出的有序列表,先进入的元素总是存在于栈底。删除和加入新元素都是在栈顶进行操作的
在这里插入图片描述
简单的代码实现如下,主要就是一个固定大小的数组stack(这里假设是专门放整数的数组),一个栈顶指针top

class ArrayStack{
	private int maxSize;
	private int[] stack;
	private int top = -1;
	
	public ArrayStack(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;
	}
	//元素出栈 不用删除数据,后面新数据会覆盖,或者垃圾数据被JVM回收
	public int pop() {
		if(isEmpty()) {
			throw new RuntimeException("空的");
		}
		return stack[top--];
	}
	//打印栈
	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]);
		}
	}
}

2 用栈实现综合计算器

2.1初步想法

向计算器中输入3-12*10+2,计算结果会直接出来。之所以要用到栈,是因为运算符之间有优先级之分
韩顺平的数据结构课程用栈实现计算器的思路如下:
直观点就是,先把乘除法这种优先级比较高的解决掉,剩下的只剩加减法了,直接就可计算了。因此设置一个符号栈s1,一个数字栈s2。发现数字直接入栈,发现符号后如果符号栈空直接入栈,如果符号栈不空,对比下这个符号X和符号栈最顶端的符号,如果当前符号优先级低于或者等于栈中的符号,就说明前面那个运算符可以“肆无忌惮”地进行运算了,那就先pop出符号栈的顶端运算符和数栈的两个数字进行运算,结果入数栈,先算减少一波数字和运算符。以此类推,直到碰到等于或者级别低于X的运算符,此时X入符号栈。当所有符号数字入栈完毕,依次做运算就可得出结果。
在这里插入图片描述

2.2 发现问题

这个方法在验证上面式子的时候结果是正确的,但是如果输入 3-2*6+2的时候就会得-9,原来是最后一步弹栈计算出了问题。
最后一步之前 数栈从顶至底依次是 2, 12, 3 ,符号栈从顶至底是+ -。一步步推下来,是减号哪里出了问题。解决思路有三个:

思路一是最后弹栈计算顺序修改:可以在用两个栈,依次接收符号栈和数字栈弹出来的东西再做运算,这样就改成了正序运算。

思路二是思考下为甚么会出现这种情况,-后买你跟着+就会出问题,按照上面的思路,连续的±是不会共存的,只可能是中间的乘除法隔绝
了两边的加减法。解决思路是用while循环判断,直到碰到比当前的符号X低的时候X才入符号栈,而上面的韩老师思路只是对比了一次。

思路三是遇见减号就把下一个数字取反,同时减号变加号入栈。这个从根本上消灭了减号这个运算。这个思路碰巧解决了另一个情况,比如 9 *-6,就不会在96之间入两个符号金栈。而是入9 -6金数字栈, ×进符号栈。笔者采用了思路三。

3 代码实现

首先在栈类中加入判断运算符和比较运算符优先级的方法,然后是运算方法(式子传过来的是运算符字符串,需要转化成运算)

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;
		//由于笔者采用思路3,消灭了减号,因此下面的这个case其实没必要写
		case '-':
			res = num2 - num1;
			break;
		case '*':
			res = num1 * num2;
			break;
		case '/':
			res = num2 / num1;
			break;
		default:
			break;	
		}
		return res;
	}
public class Calculator {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		String expression = "3-12*10+2";
		ArrayStack2 numStack = new ArrayStack2(10);
		ArrayStack2 operStack = new ArrayStack2(10);
		int index = 0;//用于扫描
		int num1 = 0;
		int num2 = 0;
		int oper = 0; // 符号虽然是char,但是后面的priority方法中会自动转成int, 'a' == 97是true
		int res = 0;
		char ch = ' ';
		boolean isNegative = false;//判断下一个数字是否需要变号
		while(true) {
			ch = expression.charAt(index);
			//判断ch
			if(operStack.isOper(ch)) {
				//或者出现减号就转负数,前面变加号
				if(ch == '-') {
					isNegative = true;
					ch = '+';
				}
				if(operStack.isEmpty()) {
					//为空,直接入栈
					operStack.push(ch);
				}else {	
					if(operStack.priority(ch) <= operStack.priority(operStack.peek())) {
						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 {
				//Integer.parseInt不好使,因为要求是string
				//不能数字直接入栈,需要向后再看一下,防止多位数
				String newCh = "" + ch;
				while((index + 1) <= (expression.length() - 1) && !numStack.isOper(expression.charAt(index + 1))) {
					newCh += expression.charAt(index + 1);
					index++;
				}
				//呼应前面的负号问题
				if(isNegative) {
					numStack.push(-Integer.parseInt(newCh));
					isNegative = false;
				}else {
					numStack.push(Integer.parseInt(newCh));
				}		
			}
			index++;
			if(index >= expression.length()) {
				break;
			}
		}
		//扫描完毕 依次pop
		while(true) {
			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());
	}

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值