Conversation
| if not nums: | ||
| return | ||
|
|
||
| for i in range(2, len(nums) + 1): |
There was a problem hiding this comment.
2 重ループではなく、 1 重ループ 2 つで書いたほうが直感的だと感じました。
| for i in reversed(range(len(nums) - 1)): | ||
| for j in reversed(range(i, len(nums))): |
There was a problem hiding this comment.
個人的にあまりreversed(range())の形は見慣れないので読みにくさを感じました。
特にrange(start, end)は半区間[start, end)内の整数シーケンスを返すが、それをreversed()することで、形としては(end, start]という、左は端点を含まず右は含むという形があまり一般的ではなく認知負荷が高いと感じました。
range()のstep引数を活用し、以下でどうでしょうか。
| for i in reversed(range(len(nums) - 1)): | |
| for j in reversed(range(i, len(nums))): | |
| for i in range(len(nums) - 2, -1, -1): | |
| for j in range(len(nums) - 1, i - 1, -1): |
| - 本当ならbuilt-inの.reverse()に引数でstart, endが指定できるのが嬉しい | ||
|
|
||
| ```python | ||
| def reverse_from(nums: list[int], start: int) -> None: |
There was a problem hiding this comment.
グローバルにヘルパー関数を定義する、というのも技術的な意思決定になりますが、個人的には気が進みません。
このヘルパーであれば、Solutionクラスの(丁寧には@staticmethodをつけて)プライベート風メソッド(Pythonでは慣習的にメソッド名を_から始めることでプライベートメソッドを表現します。)とするか、Solution.nextPermutation()メソッドのinner functionとして書くかの2択が、個人的に違和感のない選択です。
また、グローバルレベルの関数やクラスの定義の間にはしばしば空行を2つ入れることになっています。関連するスタイルガイドを貼っておきます。
Surround top-level function and class definitions with two blank lines.
https://peps.python.org/pep-0008/#blank-lines
Two blank lines between top-level definitions, be they function or class definitions.
https://google.github.io/styleguide/pyguide.html#s3.5-blank-lines
There was a problem hiding this comment.
ヘルパーをどこに書くかは、人によって趣味違いそうですね。
どの言語から来たかなどにも依存するでしょう。
グローバルにといいつつも、import するときのパッケージに含めるかは __init__.py で調整できたりもしますし。
| for i in range((len(nums) - start) // 2): | ||
| left = start + i | ||
| right = len(nums) - 1 - i | ||
| nums[left], nums[right] = nums[right], nums[left] |
There was a problem hiding this comment.
動かすポインターが2つあるので、素直にループ変数を2つ考えるのもありだと思います。
個人的にはwhile left < rightでループごとにleftはインクリメント、rightはデクリメントする書き方が自然に感じます。
| reverse_from(nums, i + 1) | ||
| return | ||
| nums.reverse() | ||
| ``` |
There was a problem hiding this comment.
自分好みにリファクタさせていただくとこんなイメージです。
class Solution:
def nextPermutation(self, nums: list[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
for i in range(len(nums) - 2, -1, -1):
for j in range(len(nums) - 1, i - 1, -1):
if nums[i] >= nums[j]:
continue
nums[i], nums[j] = nums[j], nums[i]
self._reverse_range(nums, i + 1, len(nums) - 1)
return
nums.reverse()
def _reverse_range(self, nums: list[int], head: int, tail: int) -> None:
"""
Reverse nums[head : tail + 1] in-place.
"""
left = head
right = tail
while left < right:
nums[left], nums[right] = nums[right], nums[left]
left += 1
right -= 1|
|
||
| ### Complexity Analysis | ||
|
|
||
| - 時間計算量:O(N^2) |
There was a problem hiding this comment.
O(N)でもいけます。
class Solution:
def nextPermutation(self, nums: list[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
n = len(nums)
if n < 2:
return
def is_sorted_until(first, last, step=1):
for i in range(first, last, step):
if nums[i] > nums[i + step]:
return i
return last
def swap(index1, index2):
nums[index1], nums[index2] = nums[index2], nums[index1]
def reverse_in_section(left, right):
while left < right:
swap(left, right)
left += 1
right -= 1
def find_bigger(target, start, stop, step=1):
for i in range(start, stop, step):
if nums[i] > target:
return i
return stop
pivot = is_sorted_until(n - 1, 0, -1) - 1
reverse_in_section(pivot + 1, n - 1)
if pivot == -1:
return
swap_target = find_bigger(nums[pivot], pivot + 1, n, 1)
assert 0 <= pivot < swap_target < n
swap(pivot, swap_target)
問題文:https://leetcode.com/problems/next-permutation/description/