我正在尝试解决此问题SPOJ CMEXPR。我发现这个问题的算法是:
我已成功完成第一步,但在第二步中遇到问题。它不会抛出任何异常,只会给出不正确的输出。
这是我的代码:
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Stack;
class RPN{
public String expression;
public String output;
public Queue<Character> queue;
public RPN(String exp){
this.expression = exp;
processRPN();
}
private void processRPN(){
System.out.println("Original Infix Expression ==> " + expression);
Queue<Character> queue = new LinkedList<Character>();
Stack<Character> stack = new Stack<Character>();
for(int i=0; i < expression.length(); i++){
Character op = new Character(expression.charAt(i));
if(op.equals('/') || op.equals('*') || op.equals('+') || op.equals('-') || op.equals('^') || op.equals('(')){
stack.push(op);
}
else if(op == ')'){
while(!stack.peek().equals('(')){
queue.add(stack.pop());
}
stack.pop();
}
else{
queue.add(op);
}
}
while(!stack.isEmpty()){
queue.add(stack.pop());
}
displayQueue(queue);
}
private void displayQueue(Queue<Character> queue){
this.output = queue.toString();
this.queue = queue;
}
public Queue<Character> getPostFixExpressionQueue(){
return queue;
}
}
class PIN{
public Stack<String> finalOutputStack;
public Queue<Character> queue;
public static Map<Character, Integer> precedenceMap = new HashMap<Character, Integer>();
static{
precedenceMap.put('-',1);
precedenceMap.put('+',2);
precedenceMap.put('*',3);
precedenceMap.put('/',4);
precedenceMap.put('^',4);
}
public PIN(Queue<Character> queue){
this.queue = queue;
processPIN();
}
private void processPIN(){
System.out.println("Postfix to Infix Process Starts...");
Character prevOp = null, currOp = null;
Stack<String> stack = new Stack<String>();
while(!queue.isEmpty()){
Character peekCh = queue.peek();
if(peekCh.equals('/') || peekCh.equals('*') || peekCh.equals('^') || peekCh.equals('+') || peekCh.equals('-')){
if(prevOp == null && currOp == null){
prevOp = queue.peek();
currOp = queue.poll();
}
else{
prevOp = currOp;
currOp = queue.poll();
}
if(precedenceMap.get(currOp) < precedenceMap.get(prevOp)){
String operand2 = stack.pop();
String operand1 = stack.pop();
stack.push("("+operand1+")"+currOp.toString()+operand2);
}
else{
String operand2 = stack.pop();
String operand1 = stack.pop();
stack.push(operand1+currOp.toString()+operand2);
}
}
else{
stack.push(queue.poll().toString());
}
}
storeOutput(stack);
}
private void storeOutput(Stack<String> stack){
this.finalOutputStack = stack;
}
}
public class Test{
public static void main(String h[]){
RPN rpnObj = new RPN("(a+b)-(c-d)-(e/f)");
System.out.println("Postfix Notation ==> " + rpnObj.output);
PIN pinObj = new PIN(rpnObj.getPostFixExpressionQueue());
System.out.println("Expression with Minimum Parenthesis ==> " + pinObj.finalOutputStack.toString());
}
}
答案 0 :(得分:1)
您用于构建逆波兰式表示法的算法会在某些输入上产生不正确的结果。具体来说,它将所有运算符解释为右结合,并且不考虑运算符优先级。
关于右结合性,考虑输入 a-b-c
。您的算法错误地构造了 abc--
,而正确的结果是 ab-c-
。这意味着您的算法错误地将输入解释为 a-(b-c)
。这种解释是 -
的右结合解释,而左结合解释是 (a-b)-c
,它保留了输入 a-b-c
的预期含义。
关于运算符优先级,请考虑输入 a*b+c
。您的算法错误地构造了 abc+*
。这意味着您的算法赋予最后一个运算符 +
比第一个运算符 *
更高的优先级,将输入解释为 a*(b+c)
。正确的结果应该是 ab*c+
,它对应于解释 (a*b)+c
。
从中缀表示法正确构造反向波兰表示法所需的算法称为 Shunting-Yard-Algorithm。有关如何在 Java 中实现它,请参见示例 here。
一旦您获得了正确的 Reverse-Polish-Notation,您的算法就会出现另一个问题,用于重建中缀表示法。您的方法是根据某些条件在运算符的左侧插入括号。这种方法不能在所有情况下都给出正确的结果。考虑例如(a+b)*(c+d)
。 Reverse-Polish-Notation 将是 ab+cd+*
。每个添加项都需要括号,但您的方法只允许在 *
的左侧而不是右侧插入括号。
此外,您是否插入括号的条件是基于在 Reverse-Polish-Notation 的从左到右遍历中将当前运算符与前一个运算符进行比较。这种方法也是不正确的,因为之前的运营商可能与当前的运营商完全无关。再考虑ab+cd+*
。当您查看第二个 +
时,前一个运算符将是第一个 +
,但它们完全无关,因为它们出现在表达式的解析树中的不同分支。
要解决此问题,您应该首先跟踪堆栈中每个元素的前一个操作符,而不是在从左到右的遍历中跟踪前一个操作符。您可以通过扩展结果堆栈来保存一对先前的运算符和结果字符串来做到这一点。或者您可以保留第二个堆栈,仅跟踪结果堆栈中每个条目的前一个运算符。
其次,您应该对操作的左侧和右侧都应用运算符优先级检查,并相应地插入括号。请注意,插入括号的规则对于左右操作数是不对称的。
例如,当前操作数为-
时,左操作数在所有情况下都不需要括号,但如果前一个操作符是+
或{{1},则右操作数需要括号}.
这是一个例子:
-