【算法题集锦之四】--算术表达式转二叉树并还原

算术表达式,即由数字和运算符组成的算式,如(1+2)*(3-5)

一个算术表达式通常可以表示成一个二叉树,如上述式子转化成二叉树就是:


在这之前,先讲讲一个算术表达式是如何求解的。

对于一个如上所示的算术表达式,如果让人来计算的话,是很简单的,可是让计算机来计算这样的算术表达式就有点难了,上述式子被称做为”中序表达式“,即运算符在操作数的中间,计算机难以计算这样的算式是因为计算机的内存模型多用栈,或者说计算机只能顺序操作,这样就无法很好处理运算符的优先级问题。所以要把中序表达式转化成计算机容易理解的形式---即后缀表达式(又称逆波兰式)。

顾名思义,后缀表达式就是运算符在后,操作数在前的式子。(1+2)*(3-5)的后缀表达式就是12+35-*

可以发现表达式中没有括号了,也就是说计算机处理这个表达式的时候就不需要考虑优先级的问题了。

如何将一个中序表达式转化成后缀表达式呢?

1、首先准备一个栈opStack用于存储运算符,一个队列reversePolish用于存储逆波兰式

2、从左向右开始读取算术表达式的元素X,分以下情况进行不同的处理:

(1)如果X是操作数,直接入队(reversePolish)

(2)如果X是运算符:再分以下情况:

1)如果栈为空,直接入栈。

2)如果X==”(“,直接入栈。

3)如果X==”)“,则将栈里的元素逐个出栈,并入队到逆波兰式队列中,直到第一个配对的”)”出栈(注:“(”和“)”都不入队)

4)如果X是其他操作符,则依次与栈顶元素比较优先级,如果栈顶元素的优先级大于等于X,则出栈,直到栈顶元素的优先级小于X或者栈为空。

当读取完中序表达式后,转化好的逆波兰式就存储在队列里了。


计算一个逆波兰式是非常简单的,只需要遵循以下步骤。

首先准备一个栈s.

1、从左开始向右遍历逆波兰式的元素。

2、如果取到的元素是操作数,直接入栈s,如果是运算符,从栈中弹出2个数进行运算,然后把运算结果入栈

当遍历完逆波兰式时,计算结果就保存在栈里了。

举个栗子:12+35-*的计算过程

1、1入栈。------栈内情况:[1].

2、2入栈。------栈内情况:[1,2].

3、遇见运算符+,弹出1,2,做运算,得结果3,入栈。------栈内情况:[3].

4、3入栈。------栈内情况:[3,3].

5、5入栈。------栈内情况:[3,3,5].

6、遇见运算符-,弹出3,5,做运算,得结果-2,入栈。------栈内情况:[3,-2].

7、遇见运算符*,弹出-2,3,做运算,得结果-6,入栈。------栈内情况:[-6].

8、计算完毕,计算结果-6


对逆波兰式的计算步骤稍作改变,我们就可以将一个算式转化成二叉树,具体步骤如下:

首先准备一个二叉树节点栈s.

1、从左开始向右遍历逆波兰式的元素。

2、新建一个树节点p,值为当前元素的值,如果取到的元素是操作数,直接把p入栈s,如果是运算符,从栈中弹出2个节点,把第一个弹出的节点作为p的右子树,第二个弹出的节点作为p的左子树,然后把p入栈。

当遍历完逆波兰式时,树的根节点就保存在栈里了。


至于二叉树还原中序表达式就更简单了(亏我还跪在了这道面试题上····唉),很容易看出对二叉树进行中序遍历即可,但是仅中序遍历时不行的,因为还没有括号。注意到二叉树的每棵子树总是要先计算的(即子树的运算优先级大于它的父节点),所以每遍历到为运算符的节点,分别加上左右括号即可保证运算优先级的正确!

以下是代码:

package test;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;

public class MathExpression {

	static class TreeNode {
		TreeNode leftChild;
		TreeNode rightChild;
		String value;
	}

	/**
	 * 将算术表达式转化成二叉树
	 * 
	 * @param expression
	 *            为了方便,使用字符串数组来存储表达式
	 * @return 二叉树的根节点
	 */
	public static TreeNode createBinaryTree(String[] expression) {

		// 存储操作数的栈
		Stack opStack = new Stack();
		// 存储转换后的逆波兰式的队列
		Queue reversePolish = new LinkedList();
		
		for (String s : expression) {
			
			// 如果是数字
			if(isDigit(s)){
				
				reversePolish.offer(s);
			// 如果是操作符	
			} else if(isOperator(s)){
				
				//是左括号直接入栈
				if("(".equals(s)){
					
					opStack.push(s);
				// 如果是右括号 
				} else if(")".equals(s)){
					
					// 把离上一个“(”之间的操作符全部弹出来放入逆波兰式的队列中
					while(!opStack.isEmpty()){
						
						String op = opStack.peek();
						if(op.equals("(")){
							
							opStack.pop();
							break;
							
						} else{
							
							reversePolish.offer(opStack.pop());
						}
					}
				} else{
						
						while(!opStack.isEmpty()){
							// 如果栈顶元素为"("直接入栈
							if("(".equals(opStack.peek())){
								
								opStack.push(s);
								break;
								//如果栈顶元素优先级大于s
							}else if(isGreat(opStack.peek(), s)){
								
								reversePolish.offer(opStack.pop());
								
							}else if(isGreat(s, opStack.peek())){
								
								opStack.push(s);
								break;
							}
							
						}
						// 如果栈为空,直接入栈
						if(opStack.isEmpty())
							
							opStack.push(s);
				}
			} 
		}
		// 将剩余的操作符入队
		while(!opStack.isEmpty()){
			
			reversePolish.offer(opStack.pop());
		}
		Stack nodeStack = new Stack();
		// 将逆波兰式转化成二叉树
		while(!reversePolish.isEmpty()){
			
			String s = reversePolish.poll();
			// 以当前的元素的值新建一个节点
			TreeNode node = new TreeNode();
			node.value = s;
			// 如果是数字
			if(isDigit(s)){
				
				nodeStack.push(node);
			// 如果是操作符
			} else if(isOperator(s)){
				
				//从栈里弹出两个节点作为当前节点的左右子节点
				TreeNode rightNode = nodeStack.pop();
				TreeNode leftNode = nodeStack.pop();
				node.leftChild = leftNode;
				node.rightChild = rightNode;
				// 入栈
				nodeStack.push(node);
			}
			
		}
		
		return nodeStack.pop();
	}

	/**
	 * 判断是否为运算符(暂时只判断四则运算的运算符)
	 * 
	 * @param s
	 * @return
	 */
	static boolean isOperator(String s) {

		if ("(".equals(s) || ")".equals(s) || "+".equals(s) || "-".equals(s)
				|| "*".equals(s) || "/".equals(s))

			return true;

		else

			return false;
	}

	/**
	 * 判断是否为数字
	 * 
	 * @param s
	 * @return
	 */
	static boolean isDigit(String s) {

		for (int i = 0; i < s.length(); i++) {

			if (!Character.isDigit(s.charAt(i)))

				return false;
		}

		return true;
	}

	/**
	 * 判断op1和op2的优先级,如果op1>op2,返回true,如果op1<=op2,返回false
	 * 
	 * @param op1
	 * @param op2
	 * @return
	 */
	static boolean isGreat(String op1, String op2) {

		if (getPriority(op1) > getPriority(op2))

			return true;

		else

			return false;
	}

	/**
	 * 获取运算符的优先级
	 * 
	 * @param op
	 * @return
	 */
	static int getPriority(String op) {

		if ("+".equals(op) || "-".equals(op))

			return 1;

		else if ("*".equals(op) || "/".equals(op))

			return 2;

		else

			throw new IllegalArgumentException("Unsupported operator!");
	}
	
	/**
	 * 打印出还原的算术表达式
	 * @param root
	 */
	static void printMathExpression(TreeNode root){
		
		if(root != null){
			if(isOperator(root.value))
				System.out.print("(");
			printMathExpression(root.leftChild);
			System.out.print(root.value);
			printMathExpression(root.rightChild);
			if(isOperator(root.value))
				System.out.print(")");
		}
	}
	public static void main(String[] args) {
		
		TreeNode root = createBinaryTree(new String[]{"(", "1", "+", "2", ")", "*", "(", "3", "-", "5", ")"});
		printMathExpression(root);
	}
	
}




你可能感兴趣的:(算法)