注意:
整體思路清晰,回溯的過程大概是怎樣的
每層遞迴實際上要解決的問題是什麼
一定記住,該重置的狀態要重置,因為回溯就是相當於在每一層的遞迴中做選擇,選擇了不同的選項,在遇到阻礙或者達到條件結束遞迴之後,再想嘗試其他的選擇路徑,應當記住回退到之前的狀態。
縱向:遞迴的深度,完整地解決問題(搜尋完所有的方案/找到最優方案 )所必須完成地一整個連續過程,該過程的長度。例如,在「選/不選」遞迴(0/1遞迴)中,遞迴的深度是可選值的數量。
對於縱向思維,要有層層深入,逐漸解決問題的概念。對於橫向思維,其實就是從並列的多個選項中選擇乙個出來加入當前的方案,或者說是在岔路口選擇一條路.... 它只是考慮在當前的路口(某層的遞迴狀態中)解決了當前的問題。而不管可選項再多,只能從中選擇乙個,對於整個遞迴路徑來說,也只是解決了當前的乙個問題而已。
橫向:每一層遞迴考慮的選擇範圍,有多個選項,並且只能從這多個並列選項中選擇一條。例如: 「選/不選」遞迴(0/1遞迴);在網格中尋找路徑的題目中,每一層的遞迴可以考慮的選擇範圍是「上下左右」四個方向。
小結:把回溯問題當做是闖關遊戲,縱向就是遊戲進度在不斷的前進,而橫向就是每一關中自己做的不同選擇,做了不同的選擇就會走在不同的闖關路徑上,我們要不就是(1)在目前的闖關路徑上一闖到底(完成一次合格的搜尋,如果是最優問題,可能已經找到了解;如果是要給出所有可能的解,我們在記錄了合格解後,還需要回溯重置,再選擇不同的路徑重新探索)(2)要不就是領盒飯(因為一些限制條件,提前終止),然後回溯重置,再選擇不同的路徑重新探索。
人生,卻不能像回溯一樣,回退重選。在某個狀態下即使面臨很多選擇,我們也只能從中選擇乙個,然後繼續往前走,關於限制條件,它本身不能讓我們強制領盒飯,不過卻可以改變甚至重塑我們的生活和精神狀態。
而因為我們的時間線是永遠乙個方向延申的,實際上我們永遠都是在縱向路徑上前進,且還是時間有限的路徑上。
想要擁有更加豐富的人生體驗,也只有在路上多增設一些路口了,把那些沒有嘗試的橫向選項收納進來,emm......是這樣吧
如何控制遍歷每乙個可選值呢?
遞迴深度 對應於 可選值的遍歷
也就是,在每層遞迴裡我們做的就是:
77 組合
/**
* 77. 組合
給定兩個整數 n 和 k,返回 1 ... n 中所有可能的 k 個數的組合。
示例:輸入: n = 4, k = 2
輸出:[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
] */
// 使用回溯,可以有兩種思路
// 1. 思路一:對於每乙個數選/不選(0/1)往深層遞迴,
// 最長的遞迴深度等於n(即最開始有哪些數可以選擇),
// 在遞迴的過程中如果遇到滿足解的要求(path的長度等於k),
// 則將合法解加入res解集(即記錄合法解)。
class solution
public void dfs(int cur, int n, int k)
//沒有可選,結束遞迴
if(cur == n + 1)
return;
//選擇當前數
path.add(cur);
dfs(cur + 1, n, k);
//回溯,重置狀態
path.remove(path.size() - 1);
//不選擇當前數,進行遞迴
dfs(cur + 1, n, k);
}}// 2. 思路二:遞迴深度是k,在每一層遞迴中從可以選擇的數的集合中選擇乙個數
// 在遞迴的過程中如果遇到滿足解的要求(path的長度等於k),
// 則將合法解加入res解集(即記錄合法解)。
class solution
void dfs(int n, int k, int cur)
for(int i = cur; i < n + 1; i++)
}}
78 子集
/***
78. 子集
給定一組不含重複元素的整數陣列 nums,返回該陣列所有可能的子集(冪集)。
說明:解集不能包含重複的子集。
示例:輸入: nums = [1,2,3]
輸出:[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2], ]
*/// 有兩種回溯思路:
// 思路1:
class solution
// 明確每一層遞迴函式裡面解決的是:
// nums陣列中每乙個可選值,選/不選---->
// 所以遞迴結束的條件就清楚了,
// 在沒有其他的要求的情況下(77中有k的限制,可以提前終止),
// 結束遞迴就是nums中所有的可選值都被考慮過,即cur==nums.length
public void dfs(int nums, int cur)
path.add(nums[cur]);
dfs(nums, cur + 1);
path.remove(path.size() - 1);
dfs(nums, cur + 1);
}}// 思路2:
class solution
void dfs(int nums, int cur)
}}
90 子集ii
46 全排列
class solution
void dfs(int nums, int cur)
for(int i = cur; i < nums.length; i++)
}void swap(int nums, int cur, int i)
}
47 全排列ii Leetcode 遞迴 回溯
又稱試探法,即走不通就退回再走 當探索到某一步走不動時,發現原先選擇達不到目標,就退回一步重新選擇.用棧是否可以?數字 n 代表生成括號的對數,請你設計乙個函式,用於能夠生成所有可能的並且 有效的 括號組合。示例 1 輸入 n 3 輸出 示例 2 輸入 n 1 輸出 1 n 8 class solu...
遞迴 回溯 裝載問題
之前討論了最優裝載問題的貪心演算法,這裡討論最優裝載問題的乙個變形。1 問題描述 有一批共n個貨櫃要裝上兩艘載重量分別為c1和c2的輪船,其中貨櫃為wi,且 要求確是否有乙個合理的裝載方案可將這n個貨櫃裝上這2艘輪船?如果有,找出一種裝載方案。例如,當n 3,c1 c2 50,且w 10,40,40...
Leetcode分類 遞迴 回溯 分治
回溯是一種應用遞迴演算法,遞迴不是 題目迴圈的困難之處在於不好模擬選不選某乙個數的過程,即選了乙個數,不方便回溯到不選這個數的情況。給定一組不含重複元素的整數陣列 nums,返回該陣列所有可能的子集 冪集 說明 解集不能包含重複的子集。示例 輸入 nums 1 2,3 輸出 3 1 2 1,2,3 ...