括號匹配這個問題,說難好難,但是說簡單好像也挺簡單,主要就是看我們的思路是否清晰,條例是否清楚。
基本問題是:給定一串字符,可能包括括號、數字、字母、標點符號、空格,檢查這一串字符中的( ) ,[ ],{ }是否匹配,匹配輸出yes,反之輸出no。
我們可以先確定最基本的邏輯,就是對輸入的數一一判斷,如果是左括號就存起來,等到有有括號的時候進行配對,配對成功繼續輸入,錯了就可以退出了。
於是,數據結構自然而然就是用棧了,而我用的是順序棧。
先說說我最開始的想法啦。
1. 由於首先要讀入一串字符,而且包括空格,但是判斷的時候只能一個一個判斷,所以就先給個數組存這串字符,用下標表示單個字符。
2. 然后為了方便地控制循環,我用strlen取得了字符的長度。
3. 設置了一個result參數,用於表示括號是否匹配的狀態,1表示匹配成功,0表示不匹配。並將其初始化為1,這樣可以避免額外討論字符串中沒有括號的情況。(沒有括號也算匹配成功)
4. 接下來就是匹配環節了。遍歷我們之前的數組,遇到左括號入棧;遇到右括號,把這個右括號和出棧的值嘗試匹配,成功就繼續掃描,失敗不用掃描了,直接輸出no吧。但是由於我用result表示是否匹配,所以我選擇吧result的值變為0,然后退出循環。
5. 遍歷的循環結束后,判斷是否匹配,即判斷result為0還是為1。
看起來沒啥問題,但接着考慮一下,由於我的result初值為1,即默認匹配,會不會存在一種情況使括號即使不匹配,但是由於沒有改變result的值而導致錯誤呢?
那么來看看什么時候result會改變。僅當掃描到右括號才會進行匹配嘗試,而僅當匹配失敗才會使result為0。也就是說,不掃描到右括號,result是沒機會匹配的。
於是我們會想,只有左括號呢?如果沒有對應的右括號,即使不匹配,result仍然為1。
那么就必須額外加一個條件來排除上述情況。顯然這種情況下,棧中一定有左括號,即棧一定非空。那么反過來說,如果括號匹配,棧就一定為空。於是想到最后判斷匹配的條件是:result=1且棧為空。
關鍵代碼如下:
1 cin.getline(a, 100); 2 length = strlen(a); 3 for (int i = 0; i <= length; i++) { // 4 if (a[i] == '(' || a[i] == '[' || a[i] == '{') { //左括號入棧 5 Push(stack, a[i]); 6 } 7 else if (a[i] == ')' || a[i] == ']' || a[i] == '}') { //右括號,將其與出棧的字符嘗試匹配 8 temp = Pop(stack); 9 if (!((a[i] == ')'&&temp =='(' )|| (a[i] == ']'&&temp == '[') || (a[i] == '}'&&temp == '{'))) { 10 result = 0; //如果不匹配就將result賦0 11 break; 12 } 13 } 14 } 15 if (result == 1&&stack.base==stack.top) { //匹配一定棧空,排除無右括號匹配,只有左括號 16 cout << "yes"; 17 } 18 else { 19 cout << "no"; 20 }
需要注意的是,棧頂指針所指的一直是頂元素的上一個。因此入棧的時候,現賦值再讓棧頂指針+1;而出棧要反過來,先-1再賦值。一開始我先輸出頭指針的內容再讓頭指針減一,這就導致運行時錯誤。因為我忘了頭指針為棧頂+1,因此其所指沒有確切的值,導致非法訪問。