diff --git a/78.Subsets/78.Subsets.md b/78.Subsets/78.Subsets.md new file mode 100644 index 0000000..39e027c --- /dev/null +++ b/78.Subsets/78.Subsets.md @@ -0,0 +1,169 @@ +https://leetcode.com/problems/subsets/description/ +## STEP1 + +例えばnums = [1,2,3]のsubsetを考えるときに1を入れる入れない、2を入れる入れない、3を入れる入れないという分岐で考えるという方針でやってみた。 + +```python +class Solution: + def subsets(self, nums: List[int]) -> List[List[int]]: + all_subsets = [] + def generateSubsets(index, subset): + if index == len(nums): + all_subsets.append(list(subset)) + return + subset.append(nums[index]) + generateSubsets(index+1, subset) + subset.pop() + generateSubsets(index+1, subset) + generateSubsets(0, []) + return all_subsets +``` + +stackとwhile ループで書くとこう +```python +class Solution: + def subsets(self, nums: List[int]) -> List[List[int]]: + all_subsets = [] + status_stack = [([], 0)] + while status_stack: + subset, index = status_stack.pop() + if index == len(nums): + all_subsets.append(subset) + continue + status_stack.append((list(subset), index + 1)) + subset.append(nums[index]) + status_stack.append((list(subset), index + 1)) + return all_subsets +``` +## STEP2 + +解法を読んでると同じ再帰で書く方法でももう一つ考え方がある気がしていてこの場合は順序を考える必要がないのでsubsetを必ず昇順で書くとして、左端が1の時、2の時、3の時、つまり1から始まって2,3が含まれたり含まれなかったりするとき、2から始まって3が含まれたり含まれなかったりする時、3から始まる時みたいに分ける書き方もある。重複がある時に下の方がシンプルにかけたりする。combination sumとかと同じ考え方というか。 + + +```python +class Solution: + def subsets(self, nums: List[int]) -> List[List[int]]: + all_subsets = [] + def generateSubsets(start_index, subset): + all_subsets.append(list(subset)) + for index in range(start_index, len(nums)): + subset.append(nums[index]) + generateSubsets(index + 1, subset) + subset.pop() + generateSubsets(0, []) + return all_subsets +``` + +再帰をstackとループで書き直すとこう +```python +class Solution: + def subsets(self, nums: List[int]) -> List[List[int]]: + all_subsets = [] + status_stack = [([], 0)] + while status_stack: + subset, start_index = status_stack.pop() + all_subsets.append(subset) + if start_index == len(nums): + continue + for index in range(start_index, len(nums)): + subset.append(nums[index]) + status_stack.append((list(subset), index + 1)) + subset.pop() + return all_subsets +``` + +他の人のコードを見ていたらbit全探索というのもあった。 +bit全探索は前者の考え方と同じでon/offと考えていく。 + +```python +class Solution: + def subsets(self, nums: List[int]) -> List[List[int]]: + all_subsets = [] + for i in range(1 << len(nums)): + subset = [] + for j in range(len(nums)): + if (i >> j) & 1: + subset.append(nums[j]) + all_subsets.append(subset) + return all_subsets +``` + +後者の考え方の方が重複を弾くには良さそう +[[90. Subsets II]] + +前者の場合で重複を弾くコード + +```python +class Solution: + def subsetsWithDup(self, nums: list[int]) -> list[list[int]]: + all_subsets = [] + nums.sort() # 重複を隣り合わせるためにソートは必須 + + def generateSubsets(subset, index): + if index == len(nums): + all_subsets.append(list(subset)) + return + + # パターンA: nums[index] を「選ぶ」世界線 + subset.append(nums[index]) + generateSubsets(subset, index + 1) + subset.pop() # バックトラック + + # パターンB: nums[index] を「選ばない」世界線 + # 「選ばない」と決めたなら、次に続く同じ数字もすべてスキップしないと重複する + next_index = index + 1 + while next_index < len(nums) and nums[next_index] == nums[index]: + next_index += 1 + + generateSubsets(subset, next_index) # スキップした位置から再開 + + generateSubsets([], 0) + return all_subsets +``` + +後者の考え方で重複を弾くバージョン +```python +class Solution: + def subsetsWithDup(self, nums: List[int]) -> List[List[int]]: + all_subsets = [] + nums.sort() + def makeSubsets(subset, start_index): + all_subsets.append(list(subset)) + for index in range(start_index, len(nums)): + if index > start_index and nums[index] == nums[index - 1]: + continue + subset.append(nums[index]) + makeSubsets(subset, index + 1) + subset.pop() + makeSubsets([], 0) + return all_subsets +``` + +## STEP3 +後者の方とbit全探索を3回ずつ練習してみる。 +```python +class Solution: + def subsets(self, nums: List[int]) -> List[List[int]]: + all_subsets = [] + def make_subsets(subset, start_index): + all_subsets.append(list(subset)) + for index in range(start_index, len(nums)): + subset.append(nums[index]) + make_subsets(subset, index + 1) + subset.pop() + make_subsets([], 0) + return all_subsets +``` + +```python +class Solution: + def subsets(self, nums: List[int]) -> List[List[int]]: + all_subsets = [] + for bit in range(1 << len(nums)): + subset = [] + for digit in range(len(nums)): + if (bit >> digit) & 1: + subset.append(nums[digit]) + all_subsets.append(list(subset)) + return all_subsets +```