個人學習記錄,不做它用。
數字 n 代表生成括號的對數,請你設計乙個函式,用於能夠生成所有可能的並且有效的括號組合。
示例:
輸入:n = 3
輸出:[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]
思路:
首先找出所有的以左括號開頭,右括號結尾的不重複的序列,形成序列集s。
找出s中所有有效的序列並輸出。
序列集s的形成方法:
為了依次輸出所有不重複的序列,這裡考慮採用樹來儲存所有分支,再以特定方式遍歷樹得到所有的序列。
第一步:生成樹
很容易的發現,所有可能的序列最開始一定以左括號開頭,這裡可以提前篩選讓暴力演算法不是那麼暴力。乙個簡單的遞迴,其中l_num和r_num的作用就是維持整體的括號數的一致,原因同上。
node*
create_treenode
(char value,
int l_num,
int r_num)
if(r_num >
0&& node->r_child ==
null
)//遞迴返回
if(l_num ==
0&& r_num ==0)
return node;
}
第二步:通過樹獲取所有可能的序列,存入乙個指標陣列中
以乙個第一步三對括號生成的樹為例,從根結點到每個葉子結點的所有路徑即為序列集s,可以看出應採用類似先序遍歷的方式,先輸出根結點,再向左或向右深入。
首先,可以讓程式判斷,當前的結點如果左右子樹都為空,則說明到了路徑的末尾,那麼就需要在輸出值之後把自己free掉,為了保證free掉的結點變成null,特設立乙個標誌位flag,每次free掉乙個結點就把flag置1,代表返回之後應該把原來的這個結點置為null,而後返回,把free掉的結點改為null,flag置0,繼續迴圈。
但是如何只遍歷一條路徑且刪除完不必要的結點之後自行退出呢?正常情況下,遍歷該樹會遍歷完左子樹之後遍歷右子樹,遍歷右子樹後自然退出,如果只遍歷一條路徑,絕對不能出現遍歷完左子樹之後遍歷右子樹的情況,所以遍歷完左子樹應該直接回溯,保證每次只走一條路。**如下:
void
print_to_array
(node *root,
char array,
int array_startnum)
if(root->r_child !=
null)}
if(root->r_child !=
null)}
if(root->l_child ==
null
&& root->r_child ==
null
)}
函式體第一行是將值輸入的陣列中儲存,其餘**如上所述。
main函式呼叫部分如下所示:
int
main
(int argc,
char
const
*ar**)
print_to_array
(root, array,0)
; print_array[i]
= array;
for(j =
0;j <
2*n;j++
)printf
("\n");
}system
("pause");
return0;
}
僅僅這樣的話,其實仍然存在問題,我們可以注意到,flag=1需要回溯到上一次才能置null,那麼對於根節點的孩子,如果它們左右子樹都為空,返回上一層就是返回到了main函式,所以一定要在main函式中也做出置null的操作。
types的值在第乙個**塊遞迴返回處體現,因為葉子結點數就是不同序列的個數。
完整**如下:
#include #include #include #define n 4 //定義括號對數
//定義結點
typedef struct tree_node
node;
static int types = 0;
static int flag = 0;
node* create_treenode(char value, int l_num, int r_num)
if (r_num > 0 && node->r_child == null)
//遞迴返回
if(l_num == 0 && r_num == 0)
return node;
}//void print_treenode(node* root)
//// printf("%c",root->value);
// if(root->r_child != null)
//}void print_to_array(node *root, char array, int array_startnum)
if(root->r_child != null)
} if(root->r_child != null)
} if(root->l_child == null && root->r_child ==null)
}int main(int argc, char const *ar**)
print_to_array(root, array, 0);
print_array[i] = array;
for(j = 0;j < 2*n;j++)
printf("\n");
} //print_treenode(root);
//printf("\n");
//printf("%d\n",types);
system("pause");
return 0;
}
驗證序列是否有效的方法:
方法一:
構造乙個棧,初始時棧中有一元素 『#』 。
遍歷序列。
如果是左括號則入棧,繼續遍歷,否則檢查棧中是否有左括號,有則彈出乙個左括號,繼續遍歷,若無,則序列無效。
當序列遍歷完成時,如果棧頂為 『#』 ,則說明序列有效,否則無效。
可以看出,如果序列長為m,最壞的情況下,棧所占用的空間為m2+
1\frac+1
2m+
1位元組(char型別佔一位元組)。
方法二:
將方法一抽象,每次遍歷到第n個元素是右括號時,檢查棧中是否存在左括號,實際就是在檢查[0,n]這個子串行中二者個數關係(每次來乙個右括號都會領乙個左括號走,可以允許左括號等右括號,因為可能後面還有右括號,但不能允許右括號等左括號,因為這樣一定有錯誤,所以左括號數量一定要大於等於右括號),在序列遍歷完成之後,如果還有剩下的左括號,那麼就是無效序列,因為不可能有右括號去匹配了。
在序列遍歷完成之前,每次見到乙個右括號,我們都需要檢查二者數量是否相等,序列遍歷完成之後,我們要看有沒有剩餘的左括號,不妨定義乙個變數difference,每次遍歷到乙個左括號,我們就把difference加一,每次來乙個右括號,我們就可以把difference減一,在每次減一之後,我們檢查一下difference是否小於0,是則序列無效,否則繼續遍歷,遍歷完成後,如果difference大於0,代表還剩左括號,序列無效,為0,剛好配對完成,序列有效。
驗證方法比較簡單,可以自行舉例驗證。
leetcode 22 括號生成 暴力法和回溯法
數字 n 代表生成括號的對數,請你設計乙個函式,用於能夠生成所有可能的並且 有效的 括號組合。示例 輸入 n 3 輸出 方法一 暴力法 相當於填滿2n個字元陣列,如果填滿後符合括號規則,則新增到答案中,每個位置都有選和不選兩種選擇,所以總共有2 2n個選擇,同時每個選擇還需要有o n 去判斷是否符合...
leetcode 括號生成
給出 n 代表生成括號的對數,請你寫出乙個函式,使其能夠生成所有可能的並且有效的括號組合。例如,給出 n 3,生成結果為 回溯法 遞迴每次填入的要麼是 要麼是 當然,第乙個肯定是 並且 的數目不會超過 記錄左括號和右括號的數量,當分別達到n時即為合格的狀態,可以壓入vector,當然為了去重最好先用...
括號生成 LeetCode
暴力法是遞迴生成所有組合方式,同時判斷是否符合要求 回溯法,不需要在生成完整的組合之後再判斷,因為在每乙個位置最多有兩種擺放方法 當前還有左括號剩餘時,放置左括號 當前組合中已經放置的左括號的個數大於右括號個數時,放置右括號 然後回退一步,逆操作狀態值 此處狀態值為臨時儲存空間 檢視接下來剩餘哪種情...