diff --git "a/\347\253\266\346\212\200\343\203\227\343\203\255\345\260\261\346\264\273\351\203\250PR\347\224\250/46. Permutations.md" "b/\347\253\266\346\212\200\343\203\227\343\203\255\345\260\261\346\264\273\351\203\250PR\347\224\250/46. Permutations.md" new file mode 100644 index 0000000..f3fce82 --- /dev/null +++ "b/\347\253\266\346\212\200\343\203\227\343\203\255\345\260\261\346\264\273\351\203\250PR\347\224\250/46. Permutations.md" @@ -0,0 +1,233 @@ +## numsをin-placeで書き換える解法 ★ + +時間計算量: O(N\*N!)
+空間計算量: O(N\*N!)
+ +```python +class Solution: + def permute(self, nums: List[int]) -> List[List[int]]: + result = [] + nums.sort() + nums_permute = nums.copy() + result.append(nums_permute[:]) + + while True: + pivot = len(nums_permute) - 2 + while pivot >= 0 and nums_permute[pivot] >= nums_permute[pivot + 1]: + pivot -= 1 + if pivot == -1: + return result + + swap = len(nums_permute) - 1 + while nums_permute[pivot] >= nums_permute[swap]: + swap -= 1 + + nums_permute[pivot], nums_permute[swap] = nums_permute[swap], nums_permute[pivot] + + nums_permute[pivot+1:] = nums_permute[pivot+1:][::-1] + result.append(nums_permute[:]) +``` + + +## swapによる解答 ★ +--- +### 1回目 +時間計算量: O(N\*N!) (リストのコピー\*再帰)
+空間計算量: O(N\*N!)
+そもそも手作業でどう解くべきかがピンと来ず、解けなかった。 +自身で手作業で書き出していく中で、入れ替えを複数行えば良いことに気がついたが、 +本番解けるかはかなり微妙。 + +```python +class Solution: + def permute(self, nums: List[int]) -> List[List[int]]: + ''' + bt(0) + ├─ bt(1) + │ ├─ bt(2) # nums = [1, 2, 3] + │ └─ bt(2) # nums = [1, 3, 2] (2と3を入れ替え) + ├─ bt(1) + │ ├─ bt(2) # nums = [2, 1, 3] (1と2を入れ替え) + │ └─ bt(2) # nums = [2, 3, 1] (1と3を入れ替え) + └─ bt(1) + ├─ bt(2) # nums = [3, 2, 1] (1と3を入れ替え、その後2と3を入れ替え) + └─ bt(2) # nums = [3, 1, 2] (1と3を入れ替え) + ''' + + all_combinations = [] + + def find_all_permutation(i): + if i == len(nums): + all_combinations.append(nums[:]) + return + for j in range(i, len(nums)): + nums[i], nums[j] = nums[j], nums[i] + find_all_permutation(i + 1) + nums[j], nums[i] = nums[i], nums[j] + + find_all_permutation(0) + return all_combinations +``` + + +### 2~3回目 +時間計算量: 1回目と同じ
+空間計算量: 1回目と同じ
+```python +class Solution: + def permute(self, nums: List[int]) -> List[List[int]]: + all_permutations = [] + + def find_all_permutation(i): + if i == len(nums): + all_permutations.append(nums[:]) + return + for j in range(i, len(nums)): + nums[i], nums[j] = nums[j], nums[i] + find_all_permutation(i + 1) + nums[j], nums[i] = nums[i], nums[j] + + find_all_permutation(0) + return all_permutations +``` + +## append,popを用いた解答 (非最適解) +--- +時間計算量: O(N\*N\*N!) (リストのコピー\*配列探索(if文)\*再帰)
+空間計算量: O(N\*N!)
+ +```python +class Solution: + def permute(self, nums: List[int]) -> List[List[int]]: + all_permutations = [] + + def find_all_permutations(permutation): + if len(permutation) == len(nums): + all_permutations.append(permutation[:]) + + for num in nums: + if num not in permutation: + permutation.append(num) + find_all_permutations(permutation) + permutation.pop() + + find_all_permutations([]) + return all_permutations +``` + +(変数を減らしたver) +```python +class Solution: + def permute(self, nums: List[int]) -> List[List[int]]: + all_permutations = [] + + def find_all_permutations(permutation): + if len(permutation) == len(nums): + all_permutations.append(permutation) + + for num in nums: + if num not in permutation: + find_all_permutations(permutation + [num]) + + find_all_permutations([]) + return all_permutations +``` + +(setを利用した最適化)★ +```python +class Solution: + def permute(self, nums: List[int]) -> List[List[int]]: + unique_nums = set(nums) + all_permutations = [] + + def find_all_permutations(permutation): + if len(permutation) == len(nums): + all_permutations.append(permutation) + + remain_nums = list(unique_nums) + for num in remain_nums: + unique_nums.remove(num) + find_all_permutations(permutation + [num]) + unique_nums.add(num) + + find_all_permutations([]) + return all_permutations +``` + +## stackを用いた解答 ★ +--- +時間計算量: O(N\*N\*N!) (numsの長さ\*remainのスライスの作成\*while(スタックが空になるまで))
+空間計算量: O(N\*N!)
+ +```python +class Solution: + def permute(self, nums: List[int]) -> List[List[int]]: + all_permutations = [] + permutation_and_remain = [([], nums)] + + while permutation_and_remain: + permutation, remain = permutation_and_remain.pop() + + if len(permutation) == len(nums): + all_permutations.append(permutation) + continue + + for i, num in enumerate(remain): + permutation_and_remain.append((permutation + [num], remain[:i] + remain[i+1:])) + + return all_permutations +``` + +## ~モジュール~ 標準ライブラリを用いた解答 +--- +時間計算量: O(N\*N!) (listへの変換\*permutation)
+空間計算量: O(N\*N!)
+ + +```python +class Solution: + def permute(self, nums: List[int]) -> List[List[int]]: + return [permutation for permutation in itertools.permutations(nums)] +``` + +List[List[int]]を返すように修正 +```python +class Solution: + def permute(self, nums: List[int]) -> List[List[int]]: + return [list(permutation) for permutation in permutations(nums)] +``` + + +https://docs.python.org/3/library/itertools.html#itertools.permutations + +itertools permutationsの内部実装再現 (documentより) +```python +def permutations(iterable, r=None): + # permutations('ABCD', 2) → AB AC AD BA BC BD CA CB CD DA DB DC + # permutations(range(3)) → 012 021 102 120 201 210 + pool = tuple(iterable) + n = len(pool) + r = n if r is None else r + if r > n: + return + indices = list(range(n)) + cycles = list(range(n, n-r, -1)) + yield tuple(pool[i] for i in indices[:r]) + while n: + for i in reversed(range(r)): + cycles[i] -= 1 + if cycles[i] == 0: + indices[i:] = indices[i+1:] + indices[i:i+1] + cycles[i] = n - i + else: + j = cycles[i] + indices[i], indices[-j] = indices[-j], indices[i] + yield tuple(pool[i] for i in indices[:r]) + break + else: + return +``` + + + + diff --git "a/\347\253\266\346\212\200\343\203\227\343\203\255\345\260\261\346\264\273\351\203\250PR\347\224\250/50. Pow(x, n).md" "b/\347\253\266\346\212\200\343\203\227\343\203\255\345\260\261\346\264\273\351\203\250PR\347\224\250/50. Pow(x, n).md" new file mode 100644 index 0000000..35840d6 --- /dev/null +++ "b/\347\253\266\346\212\200\343\203\227\343\203\255\345\260\261\346\264\273\351\203\250PR\347\224\250/50. Pow(x, n).md" @@ -0,0 +1,97 @@ +``` +負の数の割り算と余り、言語によって振る舞いが色々なので自分の使い慣れたものについては覚えておきましょう。 +``` + +## 計算式による解法 +--- +時間計算量: O(1)
+空間計算量: O(1)
+```python +class Solution: + def myPow(self, x: float, n: int) -> float: + return float(x ** n) +``` + +## 再帰による解法 (maximum recursion depth exceeded) +--- + +### 1回目 +時間計算量: O(N)
+空間計算量: O(N)
+よく考えたら、```if n == 1: return x```は不要。 +また、n == -1 は、最初にnが正負かで値の更新を行った方が、わかりやすい。 +(2回目以降で反映。) + +```python +class Solution: + def myPow(self, x: float, n: int) -> float: + if n == 0: + return 1 + if n == 1: + return x + if n == -1: + return 1 / x + + return x * self.myPow(x, n - 1) if n > 0 else 1 / x * self.myPow(x, n + 1) +``` + +### 2回目 +時間計算量: O(logN)
+空間計算量: O(logN)
+ +分割統治法のイメージで書いたが、直感的に何をしているのかわかづらい。 +```python +class Solution: + def myPow(self, x: float, n: int) -> float: + if n == 0: + return 1 + if n < 0: + x = 1 / x + n *= -1 + + divided_prod = self.myPow(x, n // 2) + if n % 2 == 0: + return divided_prod * divided_prod + else: + return divided_prod * divided_prod * x +``` + +### 3回目 +時間計算量: O(logN)
+空間計算量: O(logN)
+```python +class Solution: + def myPow(self, x: float, n: int) -> float: + if n == 0: + return 1 + if n < 0: + x = 1 /x + n *= -1 + if n % 2 == 0: + return self.myPow(x * x, n // 2) + else: + return x * self.myPow(x * x, n // 2) +``` + + + +## Interationによる解法 +--- +時間計算量: O(logN)
+空間計算量: O(logN)
+```python +class Solution: + def myPow(self, x: float, n: int) -> float: + if n < 0: + x = 1 /x + n *= -1 + + accumulated_prod = 1 + while n > 0: + if n % 2 != 0: + accumulated_prod *= x + x *= x + n //= 2 + + return accumulated_prod +```