栈的练习,以逆波兰计算器为例,结合中缀表达式,逆波兰表达式和栈

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());
	}
}
发布了133 篇原创文章 · 获赞 37 · 访问量 4730

猜你喜欢

转载自blog.csdn.net/qq_43416157/article/details/104403777