Leetcode 括號生成 暴力法

2021-10-05 01:36:38 字數 3812 閱讀 6951

個人學習記錄,不做它用。

數字 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

暴力法是遞迴生成所有組合方式,同時判斷是否符合要求 回溯法,不需要在生成完整的組合之後再判斷,因為在每乙個位置最多有兩種擺放方法 當前還有左括號剩餘時,放置左括號 當前組合中已經放置的左括號的個數大於右括號個數時,放置右括號 然後回退一步,逆操作狀態值 此處狀態值為臨時儲存空間 檢視接下來剩餘哪種情...