java有效的括号
題目:
給定一個(gè)只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判斷字符串是否有效。
有效字符串需滿足:
左括號(hào)必須用相同類型的右括號(hào)閉合。
左括號(hào)必須以正確的順序閉合。
注意空字符串可被認(rèn)為是有效字符串。
解題思路:
想象一下,你正在為你的大學(xué)課設(shè)編寫一個(gè)小型編譯器,編譯器的任務(wù)之一(或稱子任務(wù))將檢測(cè)括號(hào)是否匹配。
我們本文中看到的算法可用于處理編譯器正在編譯的程序中的所有括號(hào),并檢查是否所有括號(hào)都已配對(duì)。這將檢查給定的括號(hào)字符串是否有效,是一個(gè)重要的編程問(wèn)題。
我們這個(gè)問(wèn)題中將要處理的表達(dá)式可以包含以下三種不同類型的括號(hào):
(),
{} 以及
[]
在查看如何檢查由這些括號(hào)組成的給定表達(dá)式是否有效之前,讓我們看一下該問(wèn)題的簡(jiǎn)化版本,在簡(jiǎn)化后的問(wèn)題中,只含一種類型的括號(hào)。這么一來(lái),我們將會(huì)遇到的表達(dá)式是
(((((()))))) – VALID
()()()() – VALID
(((((((() – INVALID
((()(()))) – VALID
上我們?cè)囍靡粋€(gè)簡(jiǎn)單的算法來(lái)解決這一問(wèn)題。
我們從表達(dá)式的左側(cè)開(kāi)始,每次只處理一個(gè)括號(hào)。
假設(shè)我們遇到一個(gè)開(kāi)括號(hào)(即 (),表達(dá)式是否無(wú)效取決于在該表達(dá)式的其余部分的某處是否有相匹配的閉括號(hào)(即 ))。此時(shí),我們只是增加計(jì)數(shù)器的值保持跟蹤現(xiàn)在為止開(kāi)括號(hào)的數(shù)目。left += 1
如果我們遇到一個(gè)閉括號(hào),這可能意味著這樣兩種情況:
此閉括號(hào)沒(méi)有與與之對(duì)應(yīng)的開(kāi)括號(hào),在這種情況下,我們的表達(dá)式無(wú)效。當(dāng) left == 0,也就是沒(méi)有未配對(duì)的左括號(hào)可用時(shí)就是這種情況。
我們有一些未配對(duì)的開(kāi)括號(hào)可以與該閉括號(hào)配對(duì)。當(dāng) left > 0,也就是有未配對(duì)的左括號(hào)可用時(shí)就是這種情況。
如果我們?cè)?left == 0 時(shí)遇到一個(gè)閉括號(hào)(例如 )),那么當(dāng)前的表達(dá)式無(wú)效。否則,我們會(huì)減少 left 的值,也就是減少了可用的未配對(duì)的左括號(hào)的數(shù)量。
繼續(xù)處理字符串,直到處理完所有括號(hào)。
如果最后我們?nèi)匀挥形磁鋵?duì)的左括號(hào),這意味著表達(dá)式無(wú)效。
如果我們只是嘗試對(duì)原始問(wèn)題采用相同的辦法,這是根本就行不通的。基于簡(jiǎn)單計(jì)數(shù)器的方法能夠在上面完美運(yùn)行是因?yàn)樗欣ㄌ?hào)都具有相同的類型。因此,當(dāng)我們遇到一個(gè)閉括號(hào)時(shí),我們只需要假設(shè)有一個(gè)對(duì)應(yīng)匹配的開(kāi)括號(hào)是可用的,即假設(shè) left > 0。
但是,在我們的問(wèn)題中,如果我們遇到 ],我們真的不知道是否有相應(yīng)的 [ 可用。你可能會(huì)問(wèn):
為什么不為不同類型的括號(hào)分別維護(hù)一個(gè)單獨(dú)的計(jì)數(shù)器?
這可能不起作用,因?yàn)槔ㄌ?hào)的相對(duì)位置在這里也很重要。例如:
[{]
如果我們只是在這里維持計(jì)數(shù)器,那么只要我們遇到閉合方括號(hào),我們就會(huì)知道此處有一個(gè)可用的未配對(duì)的開(kāi)口方括號(hào)。但是,最近的未配對(duì)的開(kāi)括號(hào)是一個(gè)花括號(hào),而不是一個(gè)方括號(hào),因此計(jì)數(shù)方法在這里被打破了。
方法:棧
此外,如果仔細(xì)查看上述結(jié)構(gòu),顏色標(biāo)識(shí)的單元格將標(biāo)記開(kāi)閉的括號(hào)對(duì)。整個(gè)表達(dá)式是有效的,而它的子表達(dá)式本身也是有效的。這為問(wèn)題提供了一種遞歸結(jié)構(gòu)。例如,考慮上圖中兩個(gè)綠色括號(hào)內(nèi)的表達(dá)式。開(kāi)括號(hào)位于索引 1,相應(yīng)閉括號(hào)位于索引 6。
如果每當(dāng)我們?cè)诒磉_(dá)式中遇到一對(duì)匹配的括號(hào)時(shí),我們只是從表達(dá)式中刪除它,會(huì)發(fā)生什么?
在表示問(wèn)題的遞歸結(jié)構(gòu)時(shí),棧數(shù)據(jù)結(jié)構(gòu)可以派上用場(chǎng)。我們無(wú)法真正地從內(nèi)到外處理這個(gè)問(wèn)題,因?yàn)槲覀儗?duì)整體結(jié)構(gòu)一無(wú)所知。但是,棧可以幫助我們遞歸地處理這種情況,即從外部到內(nèi)部。
讓我們看看使用棧作為該問(wèn)題的中間數(shù)據(jù)結(jié)構(gòu)的算法。
算法
初始化棧 S。
一次處理表達(dá)式的每個(gè)括號(hào)。
如果遇到開(kāi)括號(hào),我們只需將其推到棧上即可。這意味著我們將稍后處理它,讓我們簡(jiǎn)單地轉(zhuǎn)到前面的 子表達(dá)式。
如果我們遇到一個(gè)閉括號(hào),那么我們檢查棧頂?shù)脑亍H绻麠m數(shù)脑厥且粋€(gè) 相同類型的 左括號(hào),那么我們將它從棧中彈出并繼續(xù)處理。否則,這意味著表達(dá)式無(wú)效。
如果到最后我們剩下的棧中仍然有元素,那么這意味著表達(dá)式無(wú)效。
我們來(lái)看一下該算法的動(dòng)畫(huà)演示,然后轉(zhuǎn)到實(shí)現(xiàn)部分。
代碼實(shí)例:
public class program2test {// Hash table that takes care of the mappings.private HashMap<Character, Character> mappings;// Initialize hash map with mappings. This simply makes the code easier to// read.public program2test() {this.mappings = new HashMap<Character, Character>();this.mappings.put(')', '(');this.mappings.put('}', '{');this.mappings.put(']', '[');}public boolean isValid(String s) {// Initialize a stack to be used in the algorithm.Stack<Character> stack = new Stack<Character>();for (int i = 0; i < s.length(); i++) {char c = s.charAt(i);// If the current character is a closing bracket.if (this.mappings.containsKey(c)) {// Get the top element of the stack. If the stack is empty, set// a dummy value of '#'char topElement = stack.empty() ? '#' : stack.pop();// If the mapping for this bracket doesn't match the stack's top// element, return false.//this.mappings.get(c)是獲取c鍵的值if (topElement != this.mappings.get(c)) {return false;}} else {// If it was an opening bracket, push to the stack.stack.push(c);}}// If the stack still contains elements, then it is an invalid// expression.return stack.isEmpty();}public static void main(String[] args) {program2test n = new program2test();String s = "{{{}}}";System.out.println(n.isValid(s));} }總結(jié)
- 上一篇: Java 什么叫做实例化
- 下一篇: Java 洛谷 P1002 过河卒讲解