【演算法】求全排列 回溯 交換 DFS JAVA
阿新 • • 發佈:2018-12-24
思路簡述:
一個全排列其實就是一條把陣列無重複遍歷一遍的DFS過程
思路一:簡單回溯,
1. 一個List存遍歷路徑,從第N個“結點”到第N+1個“結點”是隻需要找一個未遍歷的結點就行
2. 一個關鍵點在於查詢 下一個可遍歷“結點”, 可以用SET輔助List存放已遍歷結點,List中存遍歷書序(文中方法未用SET,複雜度較高,但是可以在Leetcode上AC);也可用一個數據結構完成:LinkedHashMap,即可儲存插入順序,也可O(1)判斷是否存在某元素。
3. 回溯: 選中某一子“結點”遞迴下去之後,要回溯查詢另一“子節點”,這就是回溯的過程,通過把某時刻路徑中最後結點刪除,新增下一“子節點”實現
程式碼
public List<List<Integer>> permute(int[] num) { List<List<Integer>> res = new LinkedList<List<Integer>>(); if(num == null || num.length < 1) return res; bt(res, new ArrayList<Integer>(), 0, num); return res; } public void bt(List<List<Integer>> res, final List<Integer> cur, int now, int[] num){ int length = num.length; if(cur.size() >= length) { res.add(new ArrayList<Integer>(){ { addAll(cur); } }); return ; } for(int i = now; i < length || i % length < now; i++){//回溯 if(cur.contains(num[i % length])) continue;//判斷回溯的元素是否已加入當前組合中 cur.add(num[i % length]); bt(res, cur, (i + 1)% length, num); cur.remove(cur.size() - 1); } }
思路二:
基於思路一,不需要輔助List存放遍歷路徑,原陣列就是遍歷路徑
每次尋找下一遍歷結點的過程可以轉化為,將後一結點交換到當前結點的過程
還原交換的過程就是回溯的過程
public List<List<Integer>> permute(int[] num) { List<List<Integer>> res = new LinkedList<List<Integer>>(); if(num == null || num.length < 1) return res; bt(res, new ArrayList<Integer>(), 0, num); return res; } public void bt(List<List<Integer>> res, final List<Integer> cur, int now, final int[] num){ int length = num.length; if(now >= length) { res.add(new ArrayList<Integer>(){ { for(int i: num) add(i); } }); return ; } // for(int i = now; i < length || i % length < now; i++){ // if(cur.contains(num[i % length])) continue; // cur.add(num[i % length]); // bt(res, cur, (i + 1)% length, num); // cur.remove(cur.size() - 1); // } for(int i = now; i < length; i++){ swap(num, now, i); bt(res, cur, now + 1, num); swap(num, now, i); } } public void swap(int[] nums, int idx1, int idx2){ int temp = nums[idx1]; nums[idx1] = nums[idx2]; nums[idx2] = temp; }