Skip to content
Open
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
50 changes: 50 additions & 0 deletions 283.Move-Zeroes/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# 283. Move Zeroes

ゼロの最初のインデックスとノンゼロの最初のインデックスを覚えておいて、それらを入れ替える操作を続ければ良い

sol1.py: この操作を素直に書いた。が、コードが長くなって可読性が低い
sol2.py: 似た操作を関数化してわかりやすくしたつもり。ただし、同じアルゴリズムなのに関数の呼び出しによって速度が落ちてしまった

sol3.py: よく考えるとscannnig indexはnonzeroに対してのみ操作を行うので、forとif文で置き換えられることに気づく


### コメントと他の方のコード

https://github.com/fhiyo/leetcode/pull/54/changes/BASE..40f6172e4c7a6b29303a6b66464dd512300ac477#diff-2f8b85074aa38861aa9dd6fbe0c5f1b540a06f8618d7552b4ffd05da21f795d3

inplaceだけど他の配列を用意しても解くことはできる。
inplaceの意味がなくなりそうだけど

解法2は変数名までほぼ同じ。自然な解法ということだろうか。クイックソートのpartitionと似ているのはなるほど。

https://github.com/fhiyo/leetcode/pull/54#discussion_r1729721970

関数化しているのも自分のと同じ考え方だ

https://github.com/olsen-blue/Arai60/pull/55#discussion_r2024137192

> Generator を使って変なコードを書いてみました
> StopIteration を捕まえるのが正しいでしょうね。
> next の第2引数の default を使えば例外がなくせますね。

Generatorを使って走査するのか。自分では思いつきそうにない。

https://github.com/shining-ai/leetcode/pull/54

> first_zero_index というか、私の感覚は、ゼロが削除された文字列の長さですね。

> https://en.wikipedia.org/wiki/Erase–remove_idiom
> Erase–remove idiom
> が頭に思い浮かんでいたら、助けになるかもしれません。

背景はこれなのかな。
remove削除するように見せかけて削除条件に合わないものを前側によせるアルゴリズム。
削除だけを行うeraseでは毎回削除した要素の後ろにある要素を全て前に詰める必要がある。
removeを行ったあとにeraseを行うと、要素を前に詰める操作を一回にまとめることができる


https://github.com/mamo3gr/arai60/blob/283_move-zeroes/283_move-zeroes/memo.md

https://github.com/mamo3gr/arai60/blob/283_move-zeroes/283_move-zeroes/step2_linked_list.py

Linked listを使って解いている
26 changes: 26 additions & 0 deletions 283.Move-Zeroes/sol1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
scannning_index = 0
while scannning_index < len(nums):
while scannning_index < len(nums) and nums[scannning_index] != 0:
scannning_index += 1
if scannning_index == len(nums):
return
first_zero_index = scannning_index
while scannning_index < len(nums) and nums[scannning_index] == 0:
scannning_index += 1
if scannning_index == len(nums):
return
while nums[first_zero_index] == 0 and scannning_index < len(nums):
nums[scannning_index], nums[first_zero_index] = (
nums[first_zero_index],
nums[scannning_index],
)
while scannning_index < len(nums) and nums[scannning_index] == 0:
scannning_index += 1
first_zero_index += 1

return
33 changes: 33 additions & 0 deletions 283.Move-Zeroes/sol2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from unittest import skip


class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""

def skip_index(index, zero_skip=True):
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

自分なら skip_zeros と skip_non_zeros に分けるのですが、趣味の範囲だと思います。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

その点は迷ったのですが、処理がほぼ同じなので今回はまとめました。
関数を分ける選択をされる方がいることは参考になります。

if zero_skip:
while index < len(nums) and nums[index] == 0:
index += 1
else:
while index < len(nums) and nums[index] != 0:
index += 1
return index

scanning_index = 0
while scanning_index < len(nums):
first_zero_index = skip_index(scanning_index, zero_skip=False)
scanning_index = skip_index(first_zero_index, zero_skip=True)
if scanning_index == len(nums):
return
while nums[first_zero_index] == 0 and scanning_index < len(nums):
nums[scanning_index], nums[first_zero_index] = (
nums[first_zero_index],
nums[scanning_index],
)
scanning_index = skip_index(scanning_index, zero_skip=True)
first_zero_index += 1

return
18 changes: 18 additions & 0 deletions 283.Move-Zeroes/sol3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from unittest import skip


class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
last_non_zero_index = 0
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ものすごく細かい話ですが、これを-1にして、ループ内で+=1してから処理をするか、それとも0にして処理後に+=1にするか、は二分探索の式の意味を決めるのと似たような感覚があるなと思いました。
変数名をこれにするなら、-1にする方が妥当かもと個人的に思いますが、まあどちらも普通ですね。
私の感覚だと0の場合はnext_non_zero_indexですが、これはこれで分かりにくいかもしれません。あるいはnon_zero_countですが、indexに使う意味がわかりにくいかもしれません。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

二分探索で閉区間か開区間かを考えるのに似ている、ということですかね。たしかに last か next か解釈は分かれそうです。今回は半開区間に対応していそうで、私の意図はこれだったのでそのままにしておきます。

shining-ai/leetcode#54

first_zero_index というか、私の感覚は、ゼロが削除された文字列の長さですね。

というコメントもありましたので変数名に count を用いるのも一理あるな、と思いました

for index in range(len(nums)):
if nums[index] != 0:
nums[index], nums[last_non_zero_index] = (
nums[last_non_zero_index],
nums[index],
)
last_non_zero_index += 1

return