这篇是基于我之前的一篇文章的:LintCode数据结构题 那篇文章介绍了基本的堆栈实现以及一些基本的应用。现在来看一下更多的题目和应用来扩展一下对堆栈的实践。
要求对表达式进行展开。比如 s = 3[2[ad]3[pf]]xyz, return adadpfpfpfadadpfpfpfadadpfpfpfxyz。这道题可以用一个栈Stack来解决。然后通过分支判断来处理不同的情况。需要注意的是数字可以是两位数三位数等等,所以要对数字进行整合。
从左往右扫描字符串,基本的逻辑如下:
1)如遇到数字,则把字符转换为数字
2)如遇到左括号,则把数字入栈
3)如遇到右括号,则把栈内的字符串拿出来做一个转换,再放回到栈中
4)如遇到普通的字符,则入栈
举个例子,假如字符串是2[2[a]2[p]]x,则转换过程如下图所示:
public class Solution {
/**
* @param s an expression includes numbers, letters and brackets
* @return a string
*/
public String expressionExpand(String s) {
Stack<String> stack = new Stack<String>();
int number = 0;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (Character.isDigit(c)) {
number = number * 10 + (c - '0');
} else if (c == '[') {
stack.push(number + "");
number = 0;
} else if (c == ']') {
String toAdd = popStack(stack);
int count = Integer.parseInt(stack.pop());
for (int j = 0; j < count; j++) {
stack.push(toAdd);
}
} else {
stack.push(c + "");
}
}
return popStack(stack);
}
public String popStack(Stack<String> stack) {
Stack<String> buffer = new Stack<String>();
while (!stack.isEmpty() && !Character.isDigit(stack.peek().charAt(0))) {
buffer.push(stack.pop());
}
StringBuilder sb = new StringBuilder();
while (!buffer.isEmpty()) {
sb.append(buffer.pop());
}
return sb.toString();
}
}
367. Expression Tree Build
中缀表达式来构建表达式树,维基百科上有关于后缀表达式构建表达式树的算法:https://en.wikipedia.org/wiki/Binary_expression_tree 不过那个就简单很多。中缀建树就要难很多,启发于如下算法:http://www.cnblogs.com/deepblueme/p/4779514.html
我们需要用2个栈来解决,一个栈data用于存数字,一个栈op用于存操作符。从左往右扫描中缀表达式字符串:
1)若遇到数字,则new一个节点,存到data栈里面
2)若遇到左括号(,则new一个节点,存到op栈里面
3)若遇到+-,若op栈有运算符的话(不是左括号)话,那就循环的把op栈顶运算符拿出来,把data栈的2个数字拿出来,凑成一个树,然后压回data栈。循环完后,再把遇到的+-压入op栈。
4)若遇到*/,若op栈有运算符的话(不是*/)话,那就循环的把op栈顶运算符拿出来,把data栈的2个数字拿出来,凑成一个树,然后压回data栈。循环完后,再把遇到的*/压入op栈。
5)若遇到右括号,若op栈顶的运算符不是左括号的话,那就循环的把op栈顶运算符拿出来,把data栈的2个数字拿出来,凑成一个树,然后压回data栈。直到遇到左括号,然后把左括号pop出来。
有个小技巧就是可以在纸上画2个栈来模拟一下整个流程,这样就会清晰很多。不必死记硬背,随便拿出一个例子,然后onsite的时候拿出白板画一下模拟一下流程。思路整理顺畅后,代码自然就水到渠成了。
/**
* Definition of ExpressionTreeNode:
* public class ExpressionTreeNode {
* public String symbol;
* public ExpressionTreeNode left, right;
* public ExpressionTreeNode(String symbol) {
* this.symbol = symbol;
* this.left = this.right = null;
* }
* }
*/
public class Solution {
/**
* @param expression: A string array
* @return: The root of expression tree
*/
public ExpressionTreeNode build(String[] expression) {
Stack<ExpressionTreeNode> data = new Stack<ExpressionTreeNode>();
Stack<ExpressionTreeNode> op = new Stack<ExpressionTreeNode>();
for (String s : expression) {
if (Character.isDigit(s.charAt(0))) {
data.push(new ExpressionTreeNode(s));
} else if (s.equals("(")) {
op.push(new ExpressionTreeNode(s));
// +-优先级很低,需要给其他操作符让路
} else if (s.equals("+") || s.equals("-")) {
while (!op.isEmpty() && !op.peek().symbol.equals("(")) {
ExpressionTreeNode node = op.pop();
ExpressionTreeNode a = data.pop();
ExpressionTreeNode b = data.pop();
node.right = a;
node.left = b;
data.push(node);
}
op.push(new ExpressionTreeNode(s));
// */只需要给同级别的让路
} else if (s.equals("*") || s.equals("/")) {
while (!op.isEmpty() && (op.peek().symbol.equals("*") || op.peek().symbol.equals("/"))) {
ExpressionTreeNode node = op.pop();
ExpressionTreeNode a = data.pop();
ExpressionTreeNode b = data.pop();
node.right = a;
node.left = b;
data.push(node);
}
op.push(new ExpressionTreeNode(s));
} else if (s.equals(")")) {
while (!op.peek().symbol.equals("(")) {
ExpressionTreeNode node = op.pop();
ExpressionTreeNode a = data.pop();
ExpressionTreeNode b = data.pop();
node.right = a;
node.left = b;
data.push(node);
}
op.pop();
}
}
// 操作符栈还有内容
while (!op.isEmpty()) {
ExpressionTreeNode node = op.pop();
ExpressionTreeNode a = data.pop();
ExpressionTreeNode b = data.pop();
node.right = a;
node.left = b;
data.push(node);
}
if (data.isEmpty()) {
return null;
}
return data.pop();
}
}
对表达式进行求值,几乎跟上题的代码一模一样,思路也是类似的。不再赘述了。反正也是用2个栈来解决,画画图就能理解了:
public class Solution {
/**
* @param expression: an array of strings;
* @return: an integer
*/
public int calc(String op, String a, String b) {
int x = Integer.parseInt(a);
int y = Integer.parseInt(b);
if (op.equals("*")) {
return x * y;
} else if (op.equals("/")) {
return x / y;
} else if (op.equals("+")) {
return x + y;
} else if (op.equals("-")) {
return x - y;
}
return 0;
}
public int evaluateExpression(String[] expression) {
Stack<String> data = new Stack<String>();
Stack<String> op = new Stack<String>();
for (String s: expression) {
if (Character.isDigit(s.charAt(0))) {
data.push(s);
} else if (s.equals("(")) {
op.push(s);
} else if (s.equals("+") || s.equals("-")) {
while (!op.isEmpty() && !op.peek().equals("(")) {
String operator = op.pop();
String b = data.pop();
String a = data.pop();
String res = calc(operator, a, b) + "";
data.push(res);
}
op.push(s);
} else if (s.equals("*") || s.equals("/")) {
while (!op.isEmpty() && (op.peek().equals("*") || op.peek().equals("/"))) {
String operator = op.pop();
String b = data.pop();
String a = data.pop();
String res = calc(operator, a, b) + "";
data.push(res);
}
op.push(s);
} else if (s.equals(")")) {
while (!op.isEmpty() && !op.peek().equals("(")) {
String operator = op.pop();
String b = data.pop();
String a = data.pop();
String res = calc(operator, a, b) + "";
data.push(res);
}
op.pop();
}
}
while (!op.isEmpty()) {
String operator = op.pop();
String b = data.pop();
String a = data.pop();
String res = calc(operator, a, b) + "";
data.push(res);
}
if (data.isEmpty()) {
return 0;
}
return Integer.parseInt(data.pop());
}
};
369. Convert Expression to Polish Notation
把一个表达式转换为波兰表达式,其实思路很简单,就是先把他建立成一棵表达式树,建树可以用上面拿到build expression tree的代码,然后再利用先序遍历整棵树就能得到答案了:
class ExpressionTreeNode {
public String symbol;
public ExpressionTreeNode left, right;
public ExpressionTreeNode(String symbol) {
this.symbol = symbol;
this.left = this.right = null;
}
}
public class Solution {
/**
* @param expression: A string array
* @return: The Polish notation of this expression
*/
public ExpressionTreeNode build(String[] expression) {
Stack<ExpressionTreeNode> data = new Stack<ExpressionTreeNode>();
Stack<ExpressionTreeNode> op = new Stack<ExpressionTreeNode>();
for (String s : expression) {
if (Character.isDigit(s.charAt(0))) {
data.push(new ExpressionTreeNode(s));
} else if (s.equals("(")) {
op.push(new ExpressionTreeNode(s));
// +-优先级很低,需要给其他操作符让路
} else if (s.equals("+") || s.equals("-")) {
while (!op.isEmpty() && !op.peek().symbol.equals("(")) {
ExpressionTreeNode node = op.pop();
ExpressionTreeNode a = data.pop();
ExpressionTreeNode b = data.pop();
node.right = a;
node.left = b;
data.push(node);
}
op.push(new ExpressionTreeNode(s));
// */只需要给同级别的让路
} else if (s.equals("*") || s.equals("/")) {
while (!op.isEmpty() && (op.peek().symbol.equals("*") || op.peek().symbol.equals("/"))) {
ExpressionTreeNode node = op.pop();
ExpressionTreeNode a = data.pop();
ExpressionTreeNode b = data.pop();
node.right = a;
node.left = b;
data.push(node);
}
op.push(new ExpressionTreeNode(s));
} else if (s.equals(")")) {
while (!op.peek().symbol.equals("(")) {
ExpressionTreeNode node = op.pop();
ExpressionTreeNode a = data.pop();
ExpressionTreeNode b = data.pop();
node.right = a;
node.left = b;
data.push(node);
}
op.pop();
}
}
// 操作符栈还有内容
while (!op.isEmpty()) {
ExpressionTreeNode node = op.pop();
ExpressionTreeNode a = data.pop();
ExpressionTreeNode b = data.pop();
node.right = a;
node.left = b;
data.push(node);
}
if (data.isEmpty()) {
return null;
}
return data.pop();
}
public void preTraversal(ArrayList<String> res, ExpressionTreeNode root) {
if (root != null) {
res.add(root.symbol);
preTraversal(res, root.left);
preTraversal(res, root.right);
}
}
public ArrayList<String> convertToPN(String[] expression) {
ExpressionTreeNode root = build(expression);
ArrayList<String> res = new ArrayList<String>();
preTraversal(res, root);
return res;
}
}
370. Convert Expression to Reverse Polish Notation
把一个表达式转换为逆波兰表达式,其实思路很简单,就是先把他建立成一棵表达式树,建树可以用上面拿到build expression tree的代码,然后再利用后序遍历整棵树就能得到答案了:
class ExpressionTreeNode {
public String symbol;
public ExpressionTreeNode left, right;
public ExpressionTreeNode(String symbol) {
this.symbol = symbol;
this.left = this.right = null;
}
}
public class Solution {
/**
* @param expression: A string array
* @return: The Reverse Polish notation of this expression
*/
public ExpressionTreeNode build(String[] expression) {
Stack<ExpressionTreeNode> data = new Stack<ExpressionTreeNode>();
Stack<ExpressionTreeNode> op = new Stack<ExpressionTreeNode>();
for (String s : expression) {
if (Character.isDigit(s.charAt(0))) {
data.push(new ExpressionTreeNode(s));
} else if (s.equals("(")) {
op.push(new ExpressionTreeNode(s));
// +-优先级很低,需要给其他操作符让路
} else if (s.equals("+") || s.equals("-")) {
while (!op.isEmpty() && !op.peek().symbol.equals("(")) {
ExpressionTreeNode node = op.pop();
ExpressionTreeNode a = data.pop();
ExpressionTreeNode b = data.pop();
node.right = a;
node.left = b;
data.push(node);
}
op.push(new ExpressionTreeNode(s));
// */只需要给同级别的让路
} else if (s.equals("*") || s.equals("/")) {
while (!op.isEmpty() && (op.peek().symbol.equals("*") || op.peek().symbol.equals("/"))) {
ExpressionTreeNode node = op.pop();
ExpressionTreeNode a = data.pop();
ExpressionTreeNode b = data.pop();
node.right = a;
node.left = b;
data.push(node);
}
op.push(new ExpressionTreeNode(s));
} else if (s.equals(")")) {
while (!op.peek().symbol.equals("(")) {
ExpressionTreeNode node = op.pop();
ExpressionTreeNode a = data.pop();
ExpressionTreeNode b = data.pop();
node.right = a;
node.left = b;
data.push(node);
}
op.pop();
}
}
// 操作符栈还有内容
while (!op.isEmpty()) {
ExpressionTreeNode node = op.pop();
ExpressionTreeNode a = data.pop();
ExpressionTreeNode b = data.pop();
node.right = a;
node.left = b;
data.push(node);
}
if (data.isEmpty()) {
return null;
}
return data.pop();
}
public void postTraversal(ArrayList<String> res, ExpressionTreeNode root) {
if (root != null) {
postTraversal(res, root.left);
postTraversal(res, root.right);
res.add(root.symbol);
}
}
public ArrayList<String> convertToRPN(String[] expression) {
ExpressionTreeNode root = build(expression);
ArrayList<String> res = new ArrayList<String>();
postTraversal(res, root);
return res;
}
}