1、什麼是回溯算法算法
回溯算法實際上一個相似枚舉的搜索嘗試過程,主要是在搜索嘗試過程當中尋找問題的解,當發現已不知足求解條件時,就「回溯」返回,嘗試別的路徑。許多複雜的,規模較大的問題均可以使用回溯法,有「通用解題方法」的美稱。編程
回溯算法實際上一個相似枚舉的深度優先搜索嘗試過程,主要是在搜索嘗試過程當中尋找問題的解,當發現已不知足求解條件時,就「回溯」返回(也就是遞歸返回),嘗試別的路徑。數組
2、回溯算法思想數據結構
回溯法通常都用在要給出多個能夠實現最終條件的解的最終形式。回溯法要求對解要添加一些約束條件。總的來講,若是要解決一個回溯法的問題,一般要肯定三個元素:函數
一、選擇。對於每一個特定的解,確定是由一步步構建而來的,而每一步怎麼構建,確定都是有限個選擇,要怎麼選擇,這個要知道;同時,在編程時候要定下,優先或合法的每一步選擇的順序,通常是經過多個if或者for循環來排列。學習
二、條件。對於每一個特定的解的某一步,他必然要符合某個解要求符合的條件,若是不符合條件,就要回溯,其實回溯也就是遞歸調用的返回。spa
三、結束。當到達一個特定結束條件時候,就認爲這個一步步構建的解是符合要求的解了。把解存下來或者打印出來。對於這一步來講,有時候也能夠另外寫一個issolution函數來進行判斷。注意,當到達第三步後,有時候還須要構建一個數據結構,把符合要求的解存起來,便於當獲得全部解後,把解空間輸出來。這個數據結構必須是全局的,做爲參數之一傳遞給遞歸函數。設計
3、遞歸函數的參數的選擇,要遵循四個原則code
一、必需要有一個臨時變量(能夠就直接傳遞一個字面量或者常量進去)傳遞不完整的解,由於每一步選擇後,暫時還沒構成完整的解,這個時候這個選擇的不完整解,也要想辦法傳遞給遞歸函數。也就是,把每次遞歸的不一樣狀況傳遞給遞歸調用的函數。blog
二、能夠有一個全局變量,用來存儲完整的每一個解,通常是個集合容器(也不必定要有這樣一個變量,由於每次符合結束條件,不完整解就是完整解了,直接打印便可)。
三、最重要的一點,必定要在參數設計中,能夠獲得結束條件。一個選擇是能夠傳遞一個量n,也許是數組的長度,也許是數量,等等。
四、要保證遞歸函數返回後,狀態能夠恢復到遞歸前,以此達到真正回溯。
4、學習例題
1.給出 n 表明生成括號的對數,請你寫出一個函數,使其可以生成全部可能的而且有效的括號組合。
例如,給出 n = 3,生成結果爲:
[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]
2.思路
首先利用回溯枚舉出全部括號的可能性,而後進行判斷是否符合要求(n對而且是有效的括號組合),就添加到list表格中。
對於遞歸函數變量須要全局變量列表list:用來存儲符合要求的括號組合。
局部變量temp:表示當前函數的括號組成樣式。
計數器x:判斷遞歸次數,限制其底界。
總的造成括號對數n。
3.代碼(力扣中國第22題)
public List<String> generateParenthesis(int n) { List<String> list=new ArrayList<String>(); add_list(list,"(", 1, n*2); return list; } //書寫遞歸函數 public void add_list(List<String> list,String temp,int x,int n) { x++; if(x<=n) { add_list(list,temp+"(",x,n); add_list(list,temp+")",x,n); } if(x>n) { //在這裏寫判斷條件是否負荷有效的括號組合 char[] k=temp.toCharArray(); //計數器 int timer=0; for(int i=0;i<k.length;i++) { if(timer<0||timer>n/2) { return; } else { if(k[i]=='(') { timer++; }else { timer--; } } } if(timer==0) list.add(temp); } }