括號的匹配,就是給你一個表達式,比如a {b [c (d + e) / 2 - f ] + 1}, 看看里面的括號是否匹配。怎么看呢?從左到右掃描表達式,遇到左括號{[(,就把它存起來,遇到其它字符,則忽略,遇到右括號時,把前面剛加進去的左括號取出來,看是否匹配。如果匹配,就繼續向下走,如果不匹配,就是不匹配了。存什么地方呢?假設存數組。拿a {b [c (d + e) / 2 - f ] + 1} 舉例一下,從a開始,向后掃描,a是字符,忽略; { 是左括號,放到數組0位置;b是字符,忽略;[左括號,放到數組1位置;c是字符,忽略;(左括號,放到數組2的位置;d+e都是字符,忽略;)是右字符,這時就要從數組中拿出字符了,拿出哪一個呢?很顯然,是拿出位置2中的元素(,匹配嗎?()匹配,那繼續向下走,同時把2位置處的元素刪除;/2-f都是字符,忽略;]右括號,繼續從數組中拿出括號,這時要拿位置1處的元素,[,[]匹配,繼續向下走,同時把1位置處的元素刪除;+1都是字符,忽略;}右括號,繼續從數組中拿出括號,這時要拿位置0處的元素{,{}匹配,表達式沒有剩下字符了,整個表達式是匹配的。匹配過程中,可以看到左括號添加和取出,順序是相反的,添加的時候是0,1,2,取出的時候,是2,1,0. 這不就是棧嗎?重新描述一下表達式的匹配過程,遇到左括號,push()進棧,遇到右括號,pop()出棧,看pop()出來的元素是否和右括號匹配。
{([都是左括號,push進棧;)是右括號,pop(), (出棧,()匹配;繼續 ,]是右括號,pop(),[出棧,[]匹配;繼續,}右括號,pop(),{出棧,{}匹配成功;整個表達式也成功了。
再看幾個不匹配的表達式,比如{ [ ( ] ) },
{ [ (依次入棧,碰到],pop(), (出棧,(] 不匹配。再看一個 [ ( ) ] },
[ (依次入棧,碰到), pop(), (出棧,() 匹配;碰到],pop(), [出棧,[]匹配;碰到},此時,棧為空,不能再pop()了,也就是沒有左括號和}匹配。再看{ [ ( ) ],
{ [ (依次入棧,) ]依次出棧,都匹配,但是棧中還剩余一個{,也就是沒有右括號和它匹配。
經過上面的分析,括號匹配的實現也有點清晰了。創建一個棧,從左右向掃描表達式,就是循環遍歷表達式,遇到左括號,調用push()方法入棧,遇到右括號,要先判斷棧是否為空,如果為空,那肯定沒有對應的左括號,也就不匹配,直接return,不用再循環了。如果棧不為空,調用pop()方法,彈出一個左括號,看是否匹配,如果不匹配,也是return,不用再循環了,如果匹配,再繼續循環。如果循環完成了,也是匹配的,但還要判斷棧是否為空,如果為空,整個表達式就是匹配的,如果棧不為空,整個表達式是不匹配的。
創建棧,可以使用自己的實現,也可以使用Java提供的LinkedList類,LinkedList實現了棧的API
package dataStructuresAndAbstractions.stack; import java.util.LinkedList; public class BalanceChecker { public static boolean checkBalance(String expression){ LinkedList<Character> openDelimiterStack = new LinkedList<>(); int charCount = expression.length(); boolean isBanlanced = true; int index = 0; char nextChar = ' '; while (isBanlanced && (index < charCount)){ nextChar = expression.charAt(index); switch (nextChar){ case '(': case '{': case '[': openDelimiterStack.push(nextChar); break; case ')': case ']': case '}': if(openDelimiterStack.isEmpty()){ isBanlanced= false; } else { char delimiter = openDelimiterStack.pop(); isBanlanced = isPaired(delimiter, nextChar); } break; } index++; } if (!openDelimiterStack.isEmpty()){ isBanlanced = false; } return isBanlanced; } private static boolean isPaired(char open, char close){ return (open == '(' && close == ')') || (open == '[' && close == ']') || (open == '{' && close == '}'); } public static void main(String[] args) { String expression = "a {b [c (d + e)/2 - f] + 1}"; boolean isBalanced = BalanceChecker.checkBalance(expression); if (isBalanced) System.out.println(expression + " is balanced"); else System.out.println(expression + " is not balanced"); } }
計算帶有括號的算術表達式,比如"(1+((2+3)*(4*5)))", 實現的思路是,先創建兩個棧,一個是存放操作數字符,一個存放操作符字符,再從左到右循環遍歷字符串,忽略左括號,遇到數字,壓入到數字字符棧中,遇到操作符,壓入到操作符棧中,遇到右括號,彈出一個操作符和兩個操作數進行計算,計算完成后,再壓入到數字字符棧中,循環完畢,數字棧中只有一個數,這個數就是表達式的計算結果。忽略(,1壓入操作數棧,+壓入操作符棧。忽略((,2壓入操作數棧,+壓入操作符棧,3壓入操作數棧。
此時遇到了右擴號,彈出2,3,和+,執行3+2,把結果5壓入數字棧
繼續向下走,*入操作符棧,忽略(,4壓入操作數棧,*壓入操作符棧,5壓入操作數棧)))
遇到了右擴號,彈出5,4,和*,計算4*5,得出20放入操作數棧中
向下走,還是右括號,彈出20,5,和*,把20*5的結果100,放入到操作數棧中
還剩最后一個右括號,再把100,1和+彈出,把結果101放到操作數棧中,
至此,表達式遍歷完畢,操作符數棧中,只剩下一個數字,這個數字,就是表達式的計算結果。
import java.util.LinkedList; public class StackEvaluate { public static void main(String[] args) { LinkedList<String> ops = new LinkedList<>(); LinkedList<Double> vals = new LinkedList<>(); String expression = "(1+((2+3)*(4*5)))"; for (int i = 0; i < expression.length(); i++) { String s = expression.charAt(i) + ""; if (s.equals("(")) { ; } else if (s.equals("+") || s.equals("-") || s.equals("*") || s.equals("/")) { ops.push(s); } else if (s.equals(")")) { String op = ops.pop(); double v = vals.pop(); if (op.equals("+")) { v = vals.pop() + v; } else if (op.equals("-")) { v = vals.pop() - v; } else if (op.equals("*")) { v = vals.pop() * v; } else if (op.equals("/")) { v = vals.pop(); } vals.push(v); } else { vals.push(Double.parseDouble(s)); } } System.out.println(vals.pop()); } }