Skip to content

Conversation

@Satorien
Copy link
Owner

@Satorien Satorien commented Jan 6, 2026

if not nums:
return

for i in range(2, len(nums) + 1):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 重ループではなく、 1 重ループ 2 つで書いたほうが直感的だと感じました。

Comment on lines +112 to +113
for i in reversed(range(len(nums) - 1)):
for j in reversed(range(i, len(nums))):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

個人的にあまりreversed(range())の形は見慣れないので読みにくさを感じました。
特にrange(start, end)は半区間[start, end)内の整数シーケンスを返すが、それをreversed()することで、形としては(end, start]という、左は端点を含まず右は含むという形があまり一般的ではなく認知負荷が高いと感じました。

range()のstep引数を活用し、以下でどうでしょうか。

Suggested change
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:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

グローバルにヘルパー関数を定義する、というのも技術的な意思決定になりますが、個人的には気が進みません。

このヘルパーであれば、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

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ヘルパーをどこに書くかは、人によって趣味違いそうですね。
どの言語から来たかなどにも依存するでしょう。

グローバルにといいつつも、import するときのパッケージに含めるかは __init__.py で調整できたりもしますし。

Comment on lines +102 to +105
for i in range((len(nums) - start) // 2):
left = start + i
right = len(nums) - 1 - i
nums[left], nums[right] = nums[right], nums[left]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

動かすポインターが2つあるので、素直にループ変数を2つ考えるのもありだと思います。
個人的にはwhile left < rightでループごとにleftはインクリメント、rightはデクリメントする書き方が自然に感じます。

reverse_from(nums, i + 1)
return
nums.reverse()
```

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自分好みにリファクタさせていただくとこんなイメージです。

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)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants