diff --git a/doc/cheatsheet/backtrack.md b/doc/cheatsheet/backtrack.md index c35276c2..e510654e 100644 --- a/doc/cheatsheet/backtrack.md +++ b/doc/cheatsheet/backtrack.md @@ -15,8 +15,6 @@ ## 0) Concept -### 0-1) Types - - 3 things to consider : - `Route` : The choices have made - `Choice list` : The choices we can select currently @@ -31,6 +29,8 @@ - set - array +### 0-1) Types + - Problems types - Type 1) : `Subsets` (子集) @@ -139,10 +139,11 @@ _cnt[nums[i]] += 1 ``` - - Type 2) : `Permutations (排列組合)` - - LC 46 + - Type 2) : `Permutations (排列組合)` (全排列) + - Problems : LC 46, 47 + - (for loop call help func) + contains + pop(-1) - backtrack. via `contains` remove already used numbers and return all cases - - contains (visited) (or not in `cur`) + for loop + pop(-1) + help func + - NO NEED to use start_idx ```python # ... res = [] @@ -161,56 +162,73 @@ cur.pop(-1) # ... ``` - ```java - // java - // https://leetcode.com/problems/subsets/solutions/27281/a-general-approach-to-backtracking-questions-in-java-subsets-permutations-combination-sum-palindrome-partitioning/ - public List> permute(int[] nums) { - List> list = new ArrayList<>(); - // Arrays.sort(nums); // not necessary - backtrack(list, new ArrayList<>(), nums); - return list; - } - private void backtrack(List> list, List tempList, int [] nums){ - if(tempList.size() == nums.length){ - list.add(new ArrayList<>(tempList)); - } else{ - for(int i = 0; i < nums.length; i++){ - if(tempList.contains(nums[i])) continue; // element already exists, skip - tempList.add(nums[i]); - backtrack(list, tempList, nums); - tempList.remove(tempList.size() - 1); - } - } - } + - Permutations I (排列組合) + - LC 46 + ```python + # python + class Solution(object): + def permute(self, nums): + def help(cur): + if len(cur) == n_len: + if cur not in res: + res.append(list(cur)) + return + if len(cur) > n_len: + return + for i in nums: + #print ("i = " + str(i) + " cur = " + str(cur)) + if i not in cur: + cur.append(i) + help(cur) + """ + NOTE !!! : we UNDO the last op we just made (pop last element we put into array) + """ + cur.pop(-1) + # edge case + if not nums: + return [[]] + n_len = len(nums) + res = [] + help([]) + #print ("res = " + str(res)) + return res ``` - `Permutations II (排列組合)` - LC 47 - ```java - // java - // https://leetcode.com/problems/subsets/solutions/27281/a-general-approach-to-backtracking-questions-in-java-subsets-permutations-combination-sum-palindrome-partitioning/ - public List> permuteUnique(int[] nums) { - List> list = new ArrayList<>(); - Arrays.sort(nums); - backtrack(list, new ArrayList<>(), nums, new boolean[nums.length]); - return list; - } - - private void backtrack(List> list, List tempList, int [] nums, boolean [] used){ - if(tempList.size() == nums.length){ - list.add(new ArrayList<>(tempList)); - } else{ - for(int i = 0; i < nums.length; i++){ - if(used[i] || i > 0 && nums[i] == nums[i-1] && !used[i - 1]) continue; - used[i] = true; - tempList.add(nums[i]); - backtrack(list, tempList, nums, used); - used[i] = false; - tempList.remove(tempList.size() - 1); - } - } - } + ```python + # python + class Solution(object): + def permuteUnique(self, nums): + def help(res, cur, cnt): + if len(cur) == len(nums): + if cur not in res: + res.append(cur[:]) + return + if len(cur) > len(nums): + return + for x in _cnt: + #print ("i = " + str(i) + " cur = " + str(cur)) + #if i not in cur: + if _cnt[x] > 0: + cur.append(x) + _cnt[x] -= 1 + help(res, cur, _cnt) + """ + NOTE !!! : we UNDO the last op we just made (pop last element we put into array) + """ + cur.pop(-1) + _cnt[x] += 1 + # edge case + if not nums: + return [[]] + _cnt = Counter(nums) + #print ("_cnt = " + str(_cnt)) + res = [] + cur = [] + help(res, cur, _cnt) + return res ``` - Type 3) : `Combinations (組成)` diff --git a/leetcode_python/Backtracking/permutations-ii.py b/leetcode_python/Backtracking/permutations-ii.py index 84386631..59015a5f 100644 --- a/leetcode_python/Backtracking/permutations-ii.py +++ b/leetcode_python/Backtracking/permutations-ii.py @@ -1,6 +1,131 @@ -# V0 +""" -# V1 +47. Permutations II +Medium +8.1K +137 +Companies +Given a collection of numbers, nums, that might contain duplicates, return all possible unique permutations in any order. + + + +Example 1: + +Input: nums = [1,1,2] +Output: +[[1,1,2], + [1,2,1], + [2,1,1]] +Example 2: + +Input: nums = [1,2,3] +Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] + + +Constraints: + +1 <= nums.length <= 8 +-10 <= nums[i] <= 10 + +""" + +# V0 +# IDEA : BACKTRACK + LC 46 + COUNTER +# https://leetcode.com/problems/permutations-ii/editorial/ +class Solution(object): + def permuteUnique(self, nums): + def help(res, cur, cnt): + if len(cur) == len(nums): + if cur not in res: + res.append(cur[:]) + return + if len(cur) > len(nums): + return + for x in _cnt: + #print ("i = " + str(i) + " cur = " + str(cur)) + #if i not in cur: + if _cnt[x] > 0: + cur.append(x) + _cnt[x] -= 1 + help(res, cur, _cnt) + """ + NOTE !!! : we UNDO the last op we just made (pop last element we put into array) + """ + cur.pop(-1) + _cnt[x] += 1 + # edge case + if not nums: + return [[]] + _cnt = Counter(nums) + #print ("_cnt = " + str(_cnt)) + res = [] + cur = [] + help(res, cur, _cnt) + return res + +# V0' +# IDEA : BACKTRACK + LC 46 -> TLE +class Solution(object): + def permuteUnique(self, nums): + def help(res, cur, cnt): + if len(cur) == len(nums): + if cur not in res: + res.append(cur[:]) + return + if len(cur) > len(nums): + return + for i in range(len(nums)): + #print ("i = " + str(i) + " cur = " + str(cur)) + #if i not in cur: + if _cnt[nums[i]] > 0: + cur.append(nums[i]) + _cnt[nums[i]] -= 1 + help(res, cur, _cnt) + """ + NOTE !!! : we UNDO the last op we just made (pop last element we put into array) + """ + cur.pop(-1) + _cnt[nums[i]] += 1 + # edge case + if not nums: + return [[]] + _cnt = Counter(nums) + print ("_cnt = " + str(_cnt)) + res = [] + cur = [] + help(res, cur, _cnt) + #print ("res = " + str(res)) + return res + +# V1 +# IDEA : BACKTRACK + COUNTER +# https://leetcode.com/problems/permutations-ii/editorial/ +class Solution: + def permuteUnique(self, nums: List[int]) -> List[List[int]]: + results = [] + def backtrack(comb, counter): + if len(comb) == len(nums): + # make a deep copy of the resulting permutation, + # since the permutation would be backtracked later. + results.append(list(comb)) + return + + for num in counter: + if counter[num] > 0: + # add this number into the current combination + comb.append(num) + counter[num] -= 1 + # continue the exploration + backtrack(comb, counter) + # revert the choice for the next exploration + comb.pop() + counter[num] += 1 + + backtrack([], Counter(nums)) + + return results + +# V2 # https://blog.csdn.net/weixin_38111819/article/details/79131409 # https://blog.csdn.net/XX_123_1_RJ/article/details/81021815 # IDEA : DFS