package stack;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

/*
* 逆波兰计算器
*
* 背景:
* 计算机对于计算的表达式分为:
* 前缀表达式(波兰表达式),
* 中缀表达式,
* 后缀表达式(逆波兰表达式)
*
* 前缀表达式:运算符位于操作数之前,计算机对于前缀表达式的计算是:
* 从右到左扫描表达式,遇到数字就压入栈,
* 遇到运算符就弹出栈顶的两个数计算,并将结果压入栈,
* 一直重复上面的步骤得到结果
* 例如中缀表达式:5*6+4-8 ==>转换成前缀表达式: - + * 5 6 4 8
*
* 中缀表达式:这个就是我们经常用的数学表达式,中缀表达式对于人来说很好理解
* 但是对于计算机来说很难理解,因为中缀表达式里面有优先级,还有小括号
* 等等的问题,所以一般将中缀表达式转换成后缀表达式来操作
*
* 后缀表达式:运算符位于操作数之后,计算机对于后缀是按照人类的阅读方式,
* 从左到右扫描表达式,遇到数字就直接入栈,遇到运算符就弹出
* 栈顶两个数进行计算,将结果压入数栈,一直重复得到结果
*
* 中缀表达式转后缀表达式的方法:
* 1.使用两个栈,一个栈运算符栈s1,一个是转换栈s2
* 2.从左到右扫描中缀表达式
* 3.遇到数的时候就入s2栈
* 4.遇到运算符就和s1栈顶元素的优先级进行比较
* 如果s1的栈顶元素是空或者是左括号,就直接入栈
* 如果优先级比栈顶运算符优先级高,也入栈s1
* 如果是低的话就将s1栈顶的运算符弹出压入s2中,
* 然后继续和s1的栈顶元素进行比较,一直到入栈
* 5.如果遇到括号
* 左括号就直接压入栈
* 右括号就依次弹出s1的栈顶运算符并压入s2中,
* 一直出现右括号为止,将这一对括号消除掉了
* 6.重复上面的步骤到表达式完,然后将s1中剩余的运算符一次弹出压入s2中
* 7.将s2的元素弹出,这个顺序是波兰表达式,将结果再逆序就是逆波兰表达式了
*/
public class PolandCalculator
{
public static void main(String[] args) throws Exception
{
String expression = "110+(((12+36)*4)-57)*3";
List<String> list1 = Conversion(expression);
System.out.println("中缀表达式=" + list1);

List<String> list2 = change(list1);
System.out.println("后缀表达式=" + list2);
System.out.printf("结果=%d", count(list2));

}
/*
* 将中缀表达式转换成list集合里面,这样方便操作
*/
public static List<String> Conversion(String str)
{
/*
* 新建list集合,将str分开放在里面
* s是对于多位数时进行拼接使用的
*/
ArrayList<String> list = new ArrayList<String>();
char[] charArray = str.toCharArray();
String s;
char each;
for (int i = 0; i < charArray.length; i++)
{
each=charArray[i];
//如果不是数字就直接加进去
if(each>57||each<48)
{
list.add(each+"");
}
else
{
/*
* 是数字就得判断它的后面一位是不是还是数字
* 这里的s每一次都要清空
* 还有注意不要越界,下面的if条件是如果下一个位置
* 还有符号可以判断的话就更新each的值
* 注意在while循环结束后要对i进行减一处理,
* 因为i已经来到下一个位置了,而且判断不是数字,证明是运算符之类的
* 将i-1是为了下一次不会跳过数字后面的一个运算符
*/
s="";
while(i<charArray.length&&each>=48&&each<=57)
{
s=s+each;
i++;
if(i<charArray.length)
{
each=charArray[i];
}
}
list.add(s);
i--;
}
}
return list;
}

/*
* 将中缀表达式转换成后缀表达式,方法前面已经说过
*/
public static List<String> change(List<String> list) throws Exception
{
Stack<String> s1=new Stack<>();
List<String> s2=new ArrayList<>();
for(String each:list)
{
if(each.matches("\\d+")) //如果是数字
{
s2.add(each);
}
else if(each.equals("(")) //如果是(右括号
{
s1.push(each);
}
else if(each.equals(")")) //如果是)左括号
{
while(!s1.peek().equals("("))
{
s2.add(s1.pop());
}
s1.pop();
}
else //如果是运算符
{
//如果运算符比栈顶的小
while(s1.size()!=0 && !s1.peek().equals("(") && priority(s1.peek())>=priority(each))
{
s2.add(s1.pop());
}
s1.push(each);
}
}
while(s1.size()!=0)
{
s2.add(s1.pop());
}
return s2;
}
/*
* 返回优先级
*/
public static int priority(String operation) throws Exception
{
int res=0;
switch(operation)
{
//不知道为什么不可以像下面两条语句这样写?这个不是很正常的正则表达式吗?
// case "[+-]" :res=1;break;
// case "[*/]":res=2;break;
case "+" :res=1;break;
case "-" :res=1;break;
case "*":res=2;break;
case "/":res=2;break;

default:
throw new Exception("error"+operation);
}
return res;
}
/*
* 传入一个逆波兰表达式,计算结果
*/
public static int count(List<String> list)
{
Stack<String> stack=new Stack<>();
for(String each :list)
{
//如果是数字
if(each.matches("\\d+"))
{
stack.push(each);
}
//如果是运算符
else
{
int number1 = Integer.parseInt(stack.pop());
int number2 = Integer.parseInt(stack.pop());
int res=0;
if(each.equals("+"))
{
res=number1+number2;
}
else if(each.equals("-"))
{
res=number2-number1;
}
else if(each.equals("*"))
{
res=number1*number2;
}
else if(each.equals("/"))
{
res=number2/number1;
}
stack.push(res+"");
}
}
return Integer.parseInt(stack.pop());
}
}