遞迴回溯相關
遞迴回溯問題及DFS/BFS相關總結
一、遞迴
1. 何為遞迴?
遞迴,即函式(方法)自己呼叫自己,亦或者稱為套娃。
2. 遞迴的簡單模板
遞迴不能無限的進行,因為遞迴的原理跟棧有關,如果無限的呼叫遞迴就會棧溢位,所以必須在一開始就設定遞迴的終止條件。如下面這樣
public void recur(引數 0 ){
if 終止條件
return;
recur(引數 1);
}
但是不能寫成下面這樣
public void recur(引數 0 ){
recur(引數 1);
if 終止條件
return;
}
如果是這樣寫就會無限的遞迴下去,最後StackOverflow
3. 遞迴的簡單例子
3.1 階乘
public int factorial(int n){ if(n <= 1) return 1; else return n * factorial (n - 1); }
3.2 斐波那契數列
public int Fibonacci(int n){ if(n == 0) return 0; if(n == 1) return 1; else return Fibonacci(n - 1) + Fibonacci(n - 2); }
3.3 漢諾塔
public void hanoi(int n, char x, char y, char z){ if(n == 1){ System.out.printf("%c -> %c\n",x, z); } else { hanoi(n - 1,x, z, y); System.out.printf("%c -> %c\n",x, z); hanoi(n - 1,y, x, z); } }
4. 適合用遞迴的場合
如果一個問題可以拆分成更小的子問題,且子問題和原問題有相同的結構,就可以嘗試用遞迴去寫。但是要考慮空間的限制
5. 如何更好的理解遞迴
一開始接觸遞迴總歸會有點懵,因為原來寫的程式碼都是顯式的,你可以跟著程式碼理順下來,或者一步步debug,但是遞迴是隱式的(藉助棧)。
以階乘為例來輔助理解,加入要求4的階乘,用上面的程式會是怎樣的流程呢?
f(4)
->4 * f(3)
->4 * (3 * f(2))
->4 * (3 * (2 * f(1)))
->4 * (3 * (2 * 1))
->4 * (3 * 2)
->4 * 6
->24
前面4行表示的是因為沒有觸發終止條件一直向下拆分成更小的子問題的過程,後面的表示觸發終止條件後向上回彈代入的過程。遞迴就是一個先遞進再回歸的過程。如果想更好的理解遞迴,我的辦法是從遞迴的終止條件向上一級一級的推導,可能會好理解一點。
二、回溯
1. 什麼叫回溯演算法
簡單點來說回溯演算法是一種解決問題的思想,它通常要通過遞迴來實現,所以放在一起復習,而且確實一開始很不好理解,需要多琢磨。
回溯演算法的思路就是暴力求解,不停探索的解決問題思路。從起點出發,先朝著一個方向走,直到走不通的時候再回退一步重新做選擇。這種走不通就退回再走的思路成為回溯法。結合生活的一個例子來說,猜一個一位數的密碼,第一位先猜0,不對就再猜別的1-9,知道猜對。這種不停嘗試的列舉法就是回溯。
2. 回溯演算法例項
最近做題經常碰到回溯相關的題目,後面的DFS也跟回溯聯絡緊密,就剛好整理複習一下。
力扣78.子集
問題描述:給定一組不含重複元素
的整數陣列 nums,返回該陣列所有可能的子集(冪集)。
說明:解集不能包含重複的子集。
示例:
輸入: nums = [1,2,3]
輸出:
[
[1],
[1,2],
[1,2,3],
[1,3],
[2],
[2,3],
[3],
[]
]class solution{ //定義全域性變數的話就不用了給函式傳參了 //視個人習慣而定 List<List<Integer>> res = new ArrayList<>(); List<Integer> list = new ArrayList<>(); public List<List<Integer>> subsets(int[] nums){ dfs(nums, 0); return res; } /** *param nums:傳入的陣列 *param index:可以理解為層數 */ public void dfs(int[] nums, int index){ res.add(new ArrayList<>(list)); for(int i = index; i < nums.length; i++){ list.add(nums[i]); dfs(nums, i + 1); //避免分支汙染 list.remove(list.size() - 1); } } }
力扣46.全排列
問題描述:給定一個沒有重複
數字的序列,返回其所有可能的全排列。
示例:
輸入: nums = [1,2,3]
輸出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1],
]class solution{ List<List<Integer>> res = new ArrayList<>(); public List<List<Integer>> permute(int[] nums){ dfs(nums,new ArrayList<>()); return res; } public void dfs(int[] nums, List<Integer> list){ if(list.size() == nums.length) res.add(new ArrayList<>(list)); for(int i = 0; i < nums.length; i++){ if(!list.contains(nums[i])){ list.add(nums[i]); dfs(nums, list); list.remove(list.size() - 1); } } } }
力扣39.組合總數
問題描述:給定一個無重複元素
的陣列candidates
和一個目標數target
,找出candidates
中所有可以使數字和為target
的組合。
candidates
中的數字可以無限制重複被選取。
示例:
輸入: candidates = [2,3,6,7], target = 7,
所求解集:
[
[7],
[2,2,3],
]class solution{ List<List<Integer>> res = new ArrayList<>(); public List<List<Integer>> combinationSum(int[] candidates, int target){ dfs(candidates, target, new ArrayList<>()); return res; } public void dfs(int[] nums, int target, List<Integer> list, int index){ if(target == 0) res.add(new ArrayList<>(list)); for(int i = index; i < nums.length; i++){ if(nums[i] > target) continue; list.add(nums[i]); dfs(nums,target - nums[i],list,i); list.remove(list.size() - 1); } } }
相關文章
- 遞迴加回溯遞迴
- 遞迴與回溯法遞迴
- 關於遞迴和回溯的一次深入思考遞迴
- leetcode題解(遞迴和回溯法)LeetCode遞迴
- 「演算法之美系列」遞迴與回溯(JS版)演算法遞迴JS
- c++迷宮問題回溯法遞迴演算法C++遞迴演算法
- Java資料結構與演算法--遞迴和回溯Java資料結構演算法遞迴
- 回溯和遞迴實現迷宮問題(C語言)遞迴C語言
- 遞迴和尾遞迴遞迴
- 快速排序【遞迴】【非遞迴】排序遞迴
- 資料結構和演算法——遞迴-八皇后問題(回溯演算法)資料結構演算法遞迴
- 資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)資料結構二叉樹遞迴
- 遞迴遞迴
- 【大爽python演算法】遞迴演算法進化之回溯演算法(backtracking)Python演算法遞迴
- 什麼是遞迴?遞迴和迴圈的異同遞迴
- 二叉樹(資料結構)——利用“遞迴”思想實現相關演算法問題二叉樹資料結構遞迴演算法
- Day14 二叉樹Part2 遞迴的應用(二叉樹相關)二叉樹遞迴
- go 遞迴Go遞迴
- JavaScript遞迴JavaScript遞迴
- 分而治之-遞迴遞迴
- 理解遞迴遞迴
- 遍歷二叉樹-------遞迴&非遞迴二叉樹遞迴
- 遞迴和遞推總結遞迴
- 演算法小專欄:遞迴與尾遞迴演算法遞迴
- 迭代與遞迴--你被遞迴搞暈過嗎?遞迴
- 遞迴呼叫 VS 迴圈呼叫遞迴
- 第七章 遞迴、DFS、剪枝、回溯等問題 ------------- 7.3 題解:機器人走方格問題遞迴機器人
- 第七章 遞迴、DFS、剪枝、回溯等問題 ------------- 7.4 硬幣表示某個給定數值遞迴
- 遞迴-*快速排序遞迴排序
- 遞迴小記遞迴
- 理解遞迴 Recurtion遞迴
- C#遞迴C#遞迴
- sql server遞迴SQLServer遞迴
- Vue元件遞迴Vue元件遞迴
- SQL 遞迴思想SQL遞迴
- 遞迴函式遞迴函式
- 談談遞迴遞迴
- 遞迴問題遞迴