表达式二叉树是指:用二叉树表示表达式的运算规则,节点元素有两种含义;操作数(包含变量)和运算法。操作数节点都是叶子节点,运算符节点都是二度节点,其中子节点是参加该运算的子表达式。
其中表达式分为前缀(二叉树前序遍历)、中缀(二叉树中序遍历)、后缀(二叉树后序遍历)。
日常中算数表达式习惯的书写习惯为中缀表达式。中序遍历统计运算中先运算的一定为左子树
一般来说通过一种遍历无法确定唯一的一颗二叉树,但是算数表达式比较特殊,表达式二叉树可以根据表达式确定一颗唯一的二叉树。
1. 表达数二叉树构建
表达式构建规则如下:
创建操作符栈用来存储操作符,遍历字符串:
1.如果字符是数字,则获取后续所有的数字,创建操作数节点;
2.如果字符是操作符:
2.1 首先创建操作符节点
2.2 操作符节点一定要设置左孩子节点(操作数节点/子表达式)
2.2.1 如果栈顶操作符不为空且栈顶操作符不等于“(”,比较当前操作符与栈顶操作符的优先级,如果栈顶操作符优先级低于当前操作符,则为当前操作符设置操作数节点作为左孩子节点,当前操作符入栈;(该情况下一定有操作数节点)
2.2.2 如果栈顶操作符优先级高于或等于当前操作符,则栈顶操作符出栈,操作数节点作为栈顶操作符的右孩子节点,栈顶操作符节点作为当前操作符节点的左孩子节点,当前操作符节点入栈;
2.2.3 特殊情况(如果栈为空或者栈顶操作符为“(” -23 或(-2+3))对于这种情况,遍历至“-”时,没有操作数节点,可以创建一个值为“0”的操作数节点作为左孩子节点,然后将当前操作符节点入栈;
“(1+2)(3+4)”对于这种情况,“*”操作符无左孩子节点且左孩子节点不为“0”,栈顶操作符作为当前操作符的左孩子节点。
3.如果当前操作符是“(”,直接入栈
4.如果当前操作符是“)”,则出栈所有操作符,直到“(”为止;将操作数节点作为第一个操作符节点的右孩子节点,上层操作符节点均作为下层操作符节点的右孩子节点;
遍历结束后出栈所有操作符节点,操作数作为栈顶操作符的右孩子节点,上层操作符作为下层操作符的右孩子节点。
简单总结:入栈设置左孩子,出栈设置右孩子,遇到“(”找“)”,入栈无操作数左孩子设置为“0”,栈定操作符必有右操作数。
2. 表达式二叉树求值
利用递归方法求值,如果遇到操作符则根据当前规则计算子表达式,遇到操作数则直接返回。
3. 代码实现
public class ExpressionBinTree {
//表达式优先级
static Map<String, Integer> operator = new HashMap<String, Integer>() {
{
put("+", 1);
put("-", 1);
put("*", 2);
put("/", 2);
}
};
//二叉树
static class TreeNode {
private String val;
private TreeNode left;
private TreeNode right;
public String getVal() {
return val;
}
public void setVal(String val) {
this.val = val;
}
public TreeNode getLeft() {
return left;
}
public void setLeft(TreeNode left) {
this.left = left;
}
public TreeNode getRight() {
return right;
}
public void setRight(TreeNode right) {
this.right = right;
}
}
//根据中缀表达式创建二叉树
public static TreeNode createByInfix(String infix) {
//去除空格
infix = infix.replaceAll(" ", "");
char[] chars = infix.toCharArray();
int n = chars.length;
//操作符存储栈
LinkedList<TreeNode> stack = new LinkedList<>();
TreeNode p = null;
TreeNode child = null;
for (int i = 0; i < n; i++) {
char c = chars[i];
//"("直接入栈
if (c == '(') {
p = new TreeNode();
p.setVal(String.valueOf(c));
} else if (c == ')') {
//出栈所有操作符到"("节点为止
while (!stack.isEmpty()) {
//当前操作符作为栈顶操作符的右子树
if (!Objects.equals(stack.peek().getVal(), "(")) {
TreeNode treeNode = stack.pop();
if (child != null) {
treeNode.setRight(child);
child = null;
}
if (p != null) {
treeNode.setRight(p);
}
p = treeNode;
} else {
//"("直接出栈
stack.pop();
break;
}
}
} else {
//创建数字子节点
if (isNumber(c)) {
StringBuilder num = new StringBuilder(c + "");
int j = i + 1;
while (j < n && isNumber(chars[j])) {
num.append(chars[j++]);
}
i = j - 1;
child = new TreeNode();
child.setVal(num.toString());
} else {
//构建操作符节点p
p = new TreeNode();
p.setVal(String.valueOf(c));
TreeNode treeNode = stack.peek();
if (treeNode != null) {
if (!Objects.equals(treeNode.getVal(), "(")) {
//如果栈顶操作符优先级低于当前操作符优先级 直接入栈
if (operator.get(treeNode.getVal()) < operator.get(String.valueOf(c))) {
if (child != null) {
p.setLeft(child);
child = null;
}
} else {
//同级或栈顶操作符优先级高于当前操作符,则应该将当前操作符作为栈顶操作符的右孩子节点
if (!Objects.equals(treeNode.getVal(), "(")) {
treeNode = stack.pop();
p.setLeft(treeNode);
if (child != null) {
treeNode.setRight(child);
child = null;
}
}
}
} else {
//如果栈顶元素是"(",child == null则为操作符增加左孩子节点"0"
if (child == null) {
treeNode = new TreeNode();
treeNode.setVal("0");
p.setLeft(treeNode);
}
}
} else {
if (child == null) {
treeNode = new TreeNode();
treeNode.setVal("0");
p.setLeft(treeNode);
}
}
if (child != null) {
p.setLeft(child);
child = null;
} else if (p.getLeft() == null && !stack.isEmpty()) {
p.setLeft(stack.pop());
}
}
}
//操作符节点不为null,操作符节点入栈
if (p != null) {
stack.push(p);
p = null;
}
}
//出栈所有操作符
while (!stack.isEmpty()) {
TreeNode treeNode = stack.pop();
//child节点作为栈顶操作符的右孩子节点
if (child != null) {
treeNode.setRight(child);
child = null;
}
if (p != null) {
treeNode.setRight(p);
}
p = treeNode;
}
return p;
}
//表达式二叉树求值
static int calculator(TreeNode root) {
switch (root.val) {
case "+": return calculator(root.left) + calculator(root.right);
case "-": return calculator(root.left) - calculator(root.right);
case "*": return calculator(root.left) * calculator(root.right);
case "/": return calculator(root.left) / calculator(root.right);
default: return Integer.parseInt(root.val);
}
}
//判断字符是否是数字
static boolean isNumber(Character character) {
return Character.isDigit(character);
}
public static void main(String[] args) {
TreeNode byInfix = createByInfix("(+2)");
int res = calculator(byInfix);
System.out.println(res);
}
}