回溯演算法-子集組合排列
本文分享一些自己在刷回溯演算法-
子集組合排列
時總結的套路。
一、回溯演算法和二叉樹的聯絡
- 回溯演算法本質上是決策樹的選擇和撤銷過程,所以也屬於二叉樹。
- 回溯演算法框架中會出現for迴圈中巢狀遞迴,for是廣度搜索,遞迴是深度搜索;在二叉樹中,經常會有traverse(root.left)和traverse(root.right),其實這裡是將for迴圈給具體寫成了兩個遞迴函式進行廣度搜索,然後遞迴函式進行深度搜索。
- 回溯演算法放到二叉樹中說,相當於在前序位置和後序位置進行操作。
二、回溯演算法分類
分類規則:原陣列中的元素是否重複 | 陣列中的元素是否可以複用
1. 元素無重複並且不可複用
相關題目參考LeetCode78、77、46:
2. 元素有重複並且不可複用
相關題目參考LeetCode90、40、47:
3. 元素無重複並且可以複用
相關題目參考LeetCode39:
三、子集組合排列模組化
本節將子集組合排列模組化,在以後做題時根據題目要求選取模組組裝即可,需要做過一些子集組合排列相關回溯題目,對其有一定理解。
1. 回溯函式基本框架
List<List<Integer>> res = new LinkedList<>(); LinkedList<Integer> track = new LinkedList<>(); void backtrack(arg...) { //根據條件將路徑加入結果集 if(條件判斷) { res.add(new LinkedList<>(track)); return; } for(迴圈條件) { track.add(數值); backtrack(arg...); track.removeLast(); } }
2. 模組1:if(條件判斷)
子集:求全部的子集,不需要條件判斷
組合:需要條件判斷,一般判斷是track.size() == k
即符合組合大小K的路徑才會新增到結果集
排列:需要條件判斷,一般判斷是track.size() == nums.length
即將陣列中的元素全部新增的路徑才會新增到結果集
分析:組合和排列的判斷條件本質上是一樣的,都是取某一層的結果。
3. 模組2:for(迴圈條件)
子集:for(int i=start; i<nums.length; i++) + backtrack(nums,i+1)
組合:for(int i=start; i<nums.length; i++)/for(int i=start; i<nums.length-(k-track.size())+1; i++) + backtrack(nums,i+1)
排列:for(int i=0; i<nums.length; i++) + if(used[i]) {continue;}
分析:for迴圈的功能:1.遍歷陣列 2.在子集和組合問題和backtrack(nums,i+1)配合防止元素被重複使用。 3.在排列問題中和if(used[i]) {continue;}配合防止重複選擇
4. 模組3:arg...引數列表
子集:(1)表徵陣列:int[] nums或者int n (2)表徵start:int start
組合:(1)表徵陣列:int[] nums或者int n (2)表徵start:int start (3)表徵取第幾層集合([]集合是第一層):int k
排列:(1)表徵陣列:int[] nums
5. 模組4:假設元素可以重複
子集:(1)
Arrays.sort(nums)
:排序,讓相同的元素靠在一起 (2)在for迴圈中假如if(i>start && nums[i] == nums[i-1]) {continue;}:i>start
使nums[i-1]存在;nums[i] == nums[i-1]
跳過相同的元素
組合:同子集
排列:跟子集略有不同:if(i>start && nums[i] == nums[i-1] && !used[i-1]) {continue;}:新增!used[i-1]
是為了固定元素的相對順序,防止出現重複結果
6. 模組5:假設元素可以複用
此模組與模組2+4相反,模組2的條件都是為了元素不被複用
子集:backtrack(nums,i+1)改為backtrack(nums,i),且不加模組4的條件
組合:同子集
排列:去掉used剪枝邏輯
7. 總結:
子集和組合可以歸為一類,backtrack(nums,i+1)是防止同一元素被多次使用,if(i>start && nums[i] == nums[i-1]) 是防止出現重複的結果。
排序單獨一類:if(used[i]) {continue;}是防止同一元素被多次使用,!used[i-1]是防止出現重複結果。
排序實則是對子集和組合結果的進一步劃分,所以比子集組合的條件更加苛刻。
題目連結:
leetcode-78:子集
leetcode-77:組合
leetcode-46:全排列
leetcode-90:子集II
leetcode-40:組合總和II
leetcode-47:全排列II
leetcode-39:組合總和
宣告:本文章參考了東哥的演算法小抄,隨筆中有很多地方都沒有都沒有具體解釋,主要是作為自己的回顧隨筆,如果有不清楚的,可以先看東哥的演算法小抄。