1 題目描述
-
- 全排列
給定一個沒有重複 數字
的序列,返回其所有可能的全排列。
- 全排列
-
- 全排列 II
給定一個可包含重複數字
的序列,返回所有不重複的全排列。
- 全排列 II
2 解題思路
- 全排列
- 解決方法: 回溯法/dfs
作者:liweiwei1419
鏈接:https://leetcode-cn.com/problems/permutations/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liweiw/
來源:力扣(LeetCode)
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。
- 全排列 II
這一題是在「力扣」第 46 題: “全排列” 的基礎上增加了“序列中的元素可重複”這一條件
,但要求返回的結果又不能有重複元素。
如果要比較兩個列表是否一樣,一個很顯然的辦法是分別排序,然後逐個比對。既然要排序,我們可以在搜索之前就對候選數組排序
,一旦發現這一支搜索下去可能搜索到重複的元素就停止搜索,這樣結果集中不會包含重複元素
這個題目相比於46題,要多做的工作就是去重
可以先排序,這個方便後面剪枝操作,所以要增加排序的代碼
然後就是判斷剪枝的條件,if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1])
這裏,i>0 是爲了爲了保證 nums[i - 1] 有意義,
nums[i] == nums[i - 1],就是說該元素與前一個元素相等,這就是剪枝的關鍵,要被剪掉
寫 !used[i - 1] 是因爲 nums[i - 1] 在深度優先遍歷的過程中剛剛被撤銷選擇
還有就是循環題的第一個判斷條件,
在46題中,是if(!used[i]),但是在47題中,是if(used[i])
這也是46 和 47題的區別~~~
新加的代碼,如果已經被使用,那麼繼續下一步continue
作者:liweiwei1419
鏈接:https://leetcode-cn.com/problems/permutations-ii/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liwe-2/
來源:力扣(LeetCode)
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。
3 解決代碼
- 全排列
- 解決方法: 回溯法/dfs《Java代碼》
class Solution {
public List<List<Integer>> permute(int[] nums) {
int len = nums.length;
List<List<Integer>> res = new ArrayList<>();
//特例:如果爲空,返回的是空數組
if(len == 0){
return res;
}
//used布爾類型的數組,表示有沒有使用過
//當檢查元素是否使用的時候時間複雜度爲1, 用空間換取時間
boolean[] used = new boolean[len];
List<Integer> path = new ArrayList<>();
dfs(nums, len, 0, path, used, res);
return res;
}
public void dfs(int[] nums, int len, int depth, List<Integer> path, boolean[] used, List<List<Integer>> res){
//已經找到滿足長度的數組,可以進行返回
if(depth == len){
//這個地方要進行數據的複製,否則返回的是空
res.add(new ArrayList<>(path));
return;
}
//循環實現在,還未選擇的數中依次選擇一個元素作爲下一個位置的元素
for(int i = 0; i < len; i++){
if(!used[i]){
//沒有被用過加入到path路徑之中,把used狀態設爲true表示已使用
path.add(nums[i]);
used[i] = true;
//遞歸
dfs(nums, len, depth + 1, path, used,res);
//這一步就是完成狀態重製工作,從深層次節點回到淺層次節點
//use的狀態設爲false,表示沒有使用,並且將最後加入的那個元素移除
used[i] = false;
path.remove(path.size() - 1);
}
}
}
}
- 解決方法: 回溯法/dfs《python3代碼》
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
def dfs(nums, size, depth, path, used, res):
if depth == size:
#這個地方實現的也是元素的複製
res.append(path[:])
return
for i in range(size):
if not used[i]:
used[i] = True
path.append(nums[i])
dfs(nums, size, depth + 1, path, used, res)
used[i] = False
path.pop()
size = len(nums)
if len(nums) == 0:
return []
#布爾類型的數組used
used = [False for _ in range(size)]
res = []
# path = []
dfs(nums, size, 0, [], used,res)
return res
- 全排列II
- 解決方法: 回溯法/dfs《Java代碼》
class Solution {
public List<List<Integer>> permuteUnique(int[] nums) {
int len = nums.length;
List<List<Integer>> res = new ArrayList<>();
//特例:如果爲空,返回的是空數組
if(len == 0){
return res;
}
//-------新加的代碼,排序剪枝,方便去重-----
Arrays.sort(nums);
//used布爾類型的數組,表示有沒有使用過
//當檢查元素是否使用的時候時間複雜度爲1, 用空間換取時間
boolean[] used = new boolean[len];
List<Integer> path = new ArrayList<>();
dfs(nums, len, 0, path, used, res);
return res;
}
public void dfs(int[] nums, int len, int depth, List<Integer> path, boolean[] used, List<List<Integer>> res){
//已經找到滿足長度的數組,可以進行返回
if(depth == len){
//這個地方要進行數據的複製,否則返回的是空
res.add(new ArrayList<>(path));
return;
}
//循環實現在,還未選擇的數中依次選擇一個元素作爲下一個位置的元素
for(int i = 0; i < len; i++){
//-------新加的代碼,如果已經被使用,那麼繼續下一步-----
if(used[i]){
continue;
}
//-------新加的代碼,剪枝條件,-----
//剪枝條件:i > 0 是爲了保證 nums[i - 1] 有意義
// 寫 !used[i - 1] 是因爲 nums[i - 1] 在深度優先遍歷的過程中剛剛被撤銷選擇
if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) {
continue;
}
//沒有被用過加入到path路徑之中,把used狀態設爲true表示已使用
used[i] = true;
path.add(nums[i]);
//遞歸
dfs(nums, len, depth + 1, path, used,res);
//這一步就是完成狀態重製工作,從深層次節點回到淺層次節點
//use的狀態設爲false,表示沒有使用,並且將最後加入的那個元素移除
used[i] = false;
path.remove(path.size() - 1);
}
}
}