-
Notifications
You must be signed in to change notification settings - Fork 0
Create 31. Next Permutation.md #58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,151 @@ | ||
| # 31. Next Permutation | ||
| ## STEP1 | ||
| - 何も見ずに解いてみる | ||
| - 46\. Permutations で実装していたので書けた。ただし辞書順末尾から先頭に戻すのを忘れていた。 | ||
| ```python | ||
| class Solution: | ||
| def nextPermutation(self, nums: List[int]) -> None: | ||
| """ | ||
| Do not return anything, modify nums in-place instead. | ||
| """ | ||
| n = len(nums) | ||
| if n == 0: | ||
| return | ||
| for i in reversed(range(n - 1)): | ||
| if nums[i] < nums[i + 1]: | ||
| break | ||
| else: | ||
| nums[:] = reversed(nums) | ||
| return | ||
|
|
||
| for swap_index in reversed(range(n)): | ||
| if nums[i] < nums[swap_index]: | ||
| break | ||
| nums[i], nums[swap_index] = nums[swap_index], nums[i] | ||
| nums[i + 1:] = reversed(nums[i + 1:]) | ||
| ``` | ||
|
|
||
| ## STEP2 | ||
| ### プルリクやドキュメントを参照 | ||
| - n <= 1 のときに return の方がわかりやすい。 | ||
| - https://docs.python.org/3/reference/compound_stmts.html#the-for-statement | ||
| - for 文が一回も回らない場合も else 節は実行されるので元のコードでも正しく動く。 | ||
| - nums[:] = reversed(nums) は微妙。nums.reverse() の方が良い。 | ||
| - nums[i + 1:] = reversed(nums[i + 1:]) は nums[i + 1:] = nums[i + 1:][::-1] でも良いが、どちらでも可と思う。しかし厳密には in-place でないか。 | ||
| - 過去に実装例 https://cpprefjp.github.io/reference/algorithm/next_permutation.html を見ていたので上のようなコードになった。 | ||
| - 各処理を関数にしているPRを読んで冗長では?と思ったが、各処理の意味を読み取らせる負担を読み手に押し付けているのでよくない。 | ||
| https://discord.com/channels/1084280443945353267/1237649827240742942/1353878925117227113 | ||
| - i の寿命が長いのに、変数名に意味がなく、初期化がわかりにくいという点が問題。 | ||
| - 書き直してみる。 | ||
| ```python | ||
| class Solution: | ||
| def nextPermutation(self, nums: List[int]) -> None: | ||
| n = len(nums) | ||
| if n <= 1: | ||
| return | ||
|
|
||
| def rfind_pivot() -> int | None: | ||
| for i in reversed(range(n - 1)): | ||
| if nums[i] < nums[i + 1]: | ||
| return i | ||
| return None | ||
|
|
||
| def rfind_successor(pivot_index: int) -> int: | ||
| # There must exist an element greater than nums[pivot_index] to its right | ||
| for i in reversed(range(n)): | ||
| if nums[pivot_index] < nums[i]: | ||
| return i | ||
|
|
||
| pivot_index = rfind_pivot() | ||
| if pivot_index is None: | ||
| nums.reverse() | ||
| return | ||
|
|
||
| successor_index = rfind_successor(pivot_index) | ||
| nums[pivot_index], nums[successor_index] = nums[successor_index], nums[pivot_index] | ||
| nums[pivot_index + 1:] = reversed(nums[pivot_index + 1:]) | ||
| ``` | ||
| - https://peps.python.org/pep-0008/#:~:text=Be%20consistent%20in%20return%20statements | ||
| - return expression がある場合には return None と明示的に書くべきとのこと。rfind_pivot では return None と書く。rfind_successor では正しい使われ方をすると末尾には到達しないので return None は書かない。コメントでも補足しておく。 | ||
| - nums[pivot_index + 1:] = reversed(nums[pivot_index + 1:]) について | ||
| - reversed はイテレータを返すはずでどのように左辺にアサインされるかわからなかった。 | ||
| - https://docs.python.org/3/library/functions.html#reversed | ||
| - <https://docs.python.org/3/reference/datamodel.html#object.__setitem__> | ||
| \_\_setitem__ が呼ばれて良い感じに処理されるのではと思った。 | ||
| - https://github.com/olsen-blue/Arai60/pull/59/files | ||
| - range の step を -1 にして逆順にするの結構読みにくさを感じる。`range(n - 2, -1, -1)` と `reversed(range(n - 1))` では後者が好み。 | ||
| - https://docs.python.org/3/library/functions.html#func-range | ||
| - https://docs.python.org/3/library/stdtypes.html#typesseq-range | ||
| - https://github.com/python/cpython/blob/3.13/Objects/rangeobject.c#L1181 | ||
| - CPython には range_reverse という関数があり、`reversed(range)` の組み合わせをいい感じに (追加で多くの計算を要さずに) 処理してくれそう。 | ||
| - 2重ループで書くのも面白いですね。 | ||
| - https://en.cppreference.com/w/cpp/algorithm/next_permutation.html | ||
| - C++ の英語版refだと日本語版と違う実装例が載っている。std::is_sorted_until みたいな関数があるの意外です。関数名も自分ではつけないような感じで勉強になる。 | ||
| ## STEP3 | ||
| ### 3回ミスなく書く | ||
| ```python | ||
| class Solution: | ||
| def nextPermutation(self, nums: List[int]) -> None: | ||
| n = len(nums) | ||
| if n <= 1: | ||
| return | ||
|
|
||
| def rfind_pivot() -> int | None: | ||
| for i in reversed(range(n - 1)): | ||
| if nums[i] < nums[i + 1]: | ||
| return i | ||
| return None | ||
|
|
||
| def rfind_successor(pivot_index: int) -> int: | ||
| # There must be an element greater than nums[pivoit_index] to its right | ||
| for i in reversed(range(n)): | ||
| if nums[pivot_index] < nums[i]: | ||
| return i | ||
|
|
||
| pivot_index = rfind_pivot() | ||
| if pivot_index is None: | ||
| nums.reverse() | ||
| return | ||
|
|
||
| successor_index = rfind_successor(pivot_index) | ||
| nums[pivot_index], nums[successor_index] = nums[successor_index], nums[pivot_index] | ||
| nums[pivot_index + 1:] = reversed(nums[pivot_index + 1:]) | ||
| ``` | ||
|
|
||
| 4分,3分,4分で3回Accept | ||
|
|
||
| 3回目は reverse も関数として書きました。 | ||
| ```python | ||
| class Solution: | ||
| def nextPermutation(self, nums: List[int]) -> None: | ||
| n = len(nums) | ||
| if n <= 1: | ||
| return | ||
|
|
||
| def rfind_pivot() -> int | None: | ||
| for i in reversed(range(n - 1)): | ||
| if nums[i] < nums[i + 1]: | ||
| return i | ||
| return None | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ここで何を返すかは議論の余地があると思いますが、 https://docs.python.org/ja/3.13/library/stdtypes.html#str.rfind
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. なるほど、その観点はありませんでした。個人的には -1 はインデックスとして成立してしまうので None を返したいと思って書きました。いずれにせよdocstringに書いた方が良いですね。ありがとうございます。 |
||
|
|
||
| def rfind_successor(pivot_index: int) -> int: | ||
| # There must be an element greater than nums[pivot_index] to its right. | ||
| for i in reversed(range(n)): | ||
| if nums[pivot_index] < nums[i]: | ||
| return i | ||
|
|
||
| def reverse_in_range(left: int, right: int) -> None: | ||
| while left < right: | ||
| nums[left], nums[right] = nums[right], nums[left] | ||
| left += 1 | ||
| right -= 1 | ||
|
|
||
| pivot_index = rfind_pivot() | ||
| if pivot_index is None: | ||
| nums.reverse() | ||
| return | ||
|
|
||
| successor_index = rfind_successor(pivot_index) | ||
| nums[pivot_index], nums[successor_index] = nums[successor_index], nums[pivot_index] | ||
| reverse_in_range(pivot_index + 1, n - 1) | ||
| ``` | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
len(nums)のような定数時間でアクセスでき、かつ極めてシンプルなものをあえて変数に置くと却って読みにくくなるかもしれません。