題目描述:給定一個只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判斷字符串是否有效。
有效字符串需滿足:
左括號必須用相同類型的右括號閉合。
左括號必須以正確的順序閉合。
注意空字符串可被認爲是有效字符串。
示例 1:
輸入: “()”
輸出: true
示例 2:
輸入: “()[]{}”
輸出: true
示例 3:
輸入: “(]”
輸出: false
示例 4:
輸入: “([)]”
輸出: false
示例 5:
輸入: “{[]}”
輸出: true
算法分析:該問題中將要處理的表達式包含以下三種不同的類型的括號
- ()
- {}
- []
在查看如何檢查由這些括號組成的給定表達式是否有效之前,我們先看一下問題的化簡版本,即是隻有一種類型括號的表達式
比如
((((())(())))
首先我先闡述一下思想,設置一個變量temp=0。
- 從頭到尾掃描表達式,每次只處理一個括號。
- 假設遇到一個開括號,就是({[中的一個,temp加一,
- 如果遇到閉括號,需要分爲兩種情況分析
- 在遇到閉括號之前temp=0,說明之前沒有對應匹配的開括號,所以表達式無效
- 在遇到閉括號之前temp>0,也就是說之前的表達式有可以與閉括號配對的開括號,可以匹配的開括號數目temp減一,然後處理下一個符號。
- 繼續處理字符串,直到處理完所有的括號。
- 如果最後temp>0,表示仍然有沒配對的左括號,表達式也是無效的。
但是如果我們採用相同的辦法處理最開始的問題,是行不通的。簡單計數器之所以可以完美解決上面的問題是因爲所有的括號都是相同的類型。因此,當我們遇到一個閉括號時,我們只需要假設有一個對應匹配的開括號是可用的,即假設 left > 0。
但是,在我們的問題中,如果我們遇到 ],我們真的不知道是否有相應的 [ 可用。你可能會問:
爲什麼不爲不同類型的括號分別維護一個單獨的計數器?
這可能不起作用,因爲括號的相對位置在這裏也很重要。例如:
[{]} (False)
如果我們只是在這裏維持不同的計數器,那麼只要我們遇到閉合方括號,我們就會知道此處有一個可用的未配對的開口方括號。但是,最近的未配對的開括號是一個花括號,而不是一個方括號,因此計數方法在這裏被打破了。
方法:棧
一個有效括號表達式的子表達式也應該是有效表達式
此外,如果仔細查看上述結構,顏色標識的單元格將標記開閉的括號對。整個表達式是有效的,而它的子表達式本身也是有效的。這爲問題提供了一種遞歸結構。例如,考慮上圖中兩個綠色括號內的表達式。開括號位於索引 1,相應閉括號位於索引 6。
如果每當我們在表達式中遇到一對匹配的括號時,我們只是從表達式中刪除它,會發生什麼?
讓我們看看下面的這個想法,從整體表達式中一次刪除一個較小的表達式,因爲這是一個有效的表達式,我們最後剩留下一個空字符串。
在表示問題的遞歸結構時,棧數據結構可以派上用場。我們無法真正地從內到外處理這個問題,因爲我們對整體結構一無所知。但是,棧可以幫助我們遞歸地處理這種情況,即從外部到內部。
算法詳解
- 初始化棧s
- 一次處理表達式的每個括號
- 如果遇到開括號,推入棧。
- 如果遇到閉括號,那麼檢查棧頂元素,如果是相同類型的開括號,那麼把它從棧中彈出並繼續處理。否則,意味着表達式無效。
- 如果最後棧中還有元素,那麼意味着表達式無效。
java實現
class Solution {
private HashMap<Character,Character> maps;
public Solution(){
this.maps=new HashMap<Character, Character>();
this.maps.put(')', '(');
this.maps.put('}', '{');
this.maps.put(']', '[');
}
public boolean isValid(String s)
{
Stack<Character> stack=new Stack<Character>();
for(int i=0;i<s.length();i++)
{
char c=s.charAt(i);
if(this.maps.containsKey(c))
{
char top=stack.empty()? '#':stack.pop();
if(top!=this.maps.get(c))
return false;
}
else
{
stack.push(c);
}
}
return stack.isEmpty();
}
}
此方法***內存消耗較大***
java語言實現二:
使用棧存放左括號
算法解釋:
遇到左括號就將其壓入棧,遇到右括號就與棧頂元素對比,括號不匹配或者棧中已經不存在左括號stack.empty()爲true,說明匹配失敗。如果括號匹配說明,這一組匹配成功,它可能是嵌套在其它匹配括號中的,所以此時要將當前棧頂的左括號彈出。
如果最後最終,棧中沒有剩餘元素,也就是沒有剩下左括號,說明剛好完成匹配,括號字符串有效。否則匹配失敗,括號字符串無效。
class Solution {
public boolean isValid(String s) {
if(s.isEmpty())
return true;
Stack<Character> stack=new Stack<Character>();
for(char c:s.toCharArray()){
if(c=='(')
stack.push(')');
else if(c=='{')
stack.push('}');
else if(c=='[')
stack.push(']');
else if(stack.empty()||c!=stack.pop())
return false;
}
if(stack.empty())
return true;
return false;
}
}
c語言實現思想同於上述java方法二
bool isValid(char * s){
int p=1;
int len=strlen(s);
char temp[len+1];
for(int i=0;i<len;i++)
{
if(s[i]=='('||s[i]=='{'||s[i]=='[')
{
temp[p++]=s[i];
}
else
{
p--;
if(s[i]==')'&&temp[p]!='(')
return 0;
if(s[i]==']'&&temp[p]!='[')
return 0;
if(s[i]=='}'&&temp[p]!='{')
return 0;
}
}
return p==1;
}