Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
233 changes: 233 additions & 0 deletions 競技プロ就活部PR用/46. Permutations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
## numsをin-placeで書き換える解法 ★

時間計算量: O(N\*N!) <br>
空間計算量: O(N\*N!)<br>

```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!) (リストのコピー\*再帰)<br>
空間計算量: O(N\*N!)<br>
そもそも手作業でどう解くべきかがピンと来ず、解けなかった。
自身で手作業で書き出していく中で、入れ替えを複数行えば良いことに気がついたが、
本番解けるかはかなり微妙。

```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回目と同じ<br>
空間計算量: 1回目と同じ<br>
```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文)\*再帰)<br>
空間計算量: O(N\*N!)<br>

```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(スタックが空になるまで))<br>
空間計算量: O(N\*N!)<br>

```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)<br>
空間計算量: O(N\*N!)<br>


```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
```




97 changes: 97 additions & 0 deletions 競技プロ就活部PR用/50. Pow(x, n).md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
```
負の数の割り算と余り、言語によって振る舞いが色々なので自分の使い慣れたものについては覚えておきましょう。
```

## 計算式による解法
---
時間計算量: O(1)<br>
空間計算量: O(1)<br>
```python
class Solution:
def myPow(self, x: float, n: int) -> float:
return float(x ** n)
```

## 再帰による解法 (maximum recursion depth exceeded)
---

### 1回目
時間計算量: O(N)<br>
空間計算量: O(N)<br>
よく考えたら、```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)<br>
空間計算量: O(logN)<br>

分割統治法のイメージで書いたが、直感的に何をしているのかわかづらい。
```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)<br>
空間計算量: O(logN)<br>
```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)<br>
空間計算量: O(logN)<br>
```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
```