-
Notifications
You must be signed in to change notification settings - Fork 0
283. Move Zeroes #54
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?
283. Move Zeroes #54
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,144 @@ | ||
| # 283. Move Zeroes | ||
|
|
||
| ## 1st | ||
|
|
||
| ### ① | ||
|
|
||
| 非ゼロの要素を別のリストに避けておいてnumsを上書きする。 | ||
| 最初バブルソートっぽくO(n^2)でやろうとしたらTime Limit Exceededになり、昔この問題をやったときには通った記憶があったのでそこでなぜだと思い時間を使ってしまった (実際にそれで通したのはC++だった)。 | ||
|
|
||
| 所要時間: 14:32 | ||
|
|
||
| n: len(nums) | ||
| - 時間計算量: O(n) | ||
| - 空間計算量: O(n) | ||
|
|
||
| ```py | ||
| class Solution: | ||
| def moveZeroes(self, nums: List[int]) -> None: | ||
| nonzeros = [] | ||
| for num in nums: | ||
| if num != 0: | ||
| nonzeros.append(num) | ||
| for i in range(len(nonzeros)): | ||
| nums[i] = nonzeros[i] | ||
| for i in range(len(nonzeros), len(nums)): | ||
| nums[i] = 0 | ||
| ``` | ||
|
|
||
| ### ② | ||
|
|
||
| クイックソートのpartitionアルゴリズムに似た解法。コード内のコメントにあるのがループ不変条件で、非ゼロの要素を (順序を保ったまま) すべて左に持ってくるようにしているので解になる。 | ||
|
|
||
| 所要時間: 5:05 | ||
|
|
||
| n: len(nums) | ||
| - 時間計算量: O(n) | ||
| - 空間計算量: O(1) | ||
|
|
||
| ```py | ||
| class Solution: | ||
| def moveZeroes(self, nums: List[int]) -> None: | ||
| last_nonzero_index = 0 # any elements in nums[:last_nonzero_index] are not 0 | ||
| for i in range(len(nums)): | ||
| if nums[i] != 0: | ||
| nums[last_nonzero_index], nums[i] = nums[i], nums[last_nonzero_index] | ||
|
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. last_nonzero_indexだとそこより右にnonzeroが無いように一瞬思ってしまいましたが、それを防ぐための代案が難しいですね、、、
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. 一応上のコメントでnums[:last_nonzero_index]の各要素が非ゼロだよと補足してみましたが、名前自体変えた方が良さそうですかね? 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. コメントだとlast_nonzero_indexより左に言及していますが、パッと読んだ時には右にはnonzeroの要素がないように思ってしましました。
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. なるほど、コメントだけにして単純にleftとかにして、ループ変数をrightとする方が変に迷わせないのかもと思いました |
||
| last_nonzero_index += 1 | ||
| ``` | ||
|
|
||
| ### ③ | ||
|
|
||
| 例外処理の使い方が気持ち悪いが一応こうも書ける。 | ||
| ループを回してpop()を使っても良い。そちらの場合try-exceptは使わず書ける。その上remove()と違って毎回頭から探索しないから、あえて書くならpop()を使う方がいいな。 | ||
|
|
||
| 所要時間: 1:16 | ||
|
|
||
| n: len(nums) | ||
| - 時間計算量: O(n^2) | ||
| - 空間計算量: O(1) | ||
|
|
||
| ```py | ||
| class Solution: | ||
| def moveZeroes(self, nums: List[int]) -> None: | ||
| num_nonzeros = 0 | ||
| while True: | ||
| try: | ||
| nums.remove(0) | ||
| num_nonzeros += 1 | ||
| except ValueError: | ||
| break | ||
| nums.extend([0] * num_nonzeros) | ||
| ``` | ||
|
|
||
| `list.remove()` を確認した。 https://github.com/python/cpython/blob/35d8ac7cd7ed6cd3d84af721dce970da59bd5f68/Objects/listobject.c#L3260 | ||
|
|
||
| ループでリストの頭から各要素をremoveの引数であるvalueと比較し、等しかったら[list_ass_slice_lock_held()](https://github.com/python/cpython/blob/35d8ac7cd7ed6cd3d84af721dce970da59bd5f68/Objects/listobject.c#L833)を実行してリストからその要素を削除して終了。リスト内にvalueが存在しなければValueErrorを送出する。 | ||
|
|
||
|
|
||
| ## 2nd | ||
|
|
||
| ### 参考 | ||
|
|
||
| - https://discord.com/channels/1084280443945353267/1226508154833993788/1250091281246453800 | ||
| - https://github.com/nittoco/leetcode/pull/18 | ||
|
|
||
| ①の解法は別のリストを用意しなくても良い。こうするとほとんど②と同じになる。 | ||
|
|
||
| ```py | ||
| class Solution: | ||
| def moveZeroes(self, nums: List[int]) -> None: | ||
| last_nonzero_index = 0 | ||
| for num in nums: | ||
| if num == 0: | ||
| continue | ||
| nums[last_nonzero_index] = num | ||
| last_nonzero_index += 1 | ||
| for i in range(last_nonzero_index, len(nums)): | ||
| nums[i] = 0 | ||
| ``` | ||
|
|
||
| - https://discord.com/channels/1084280443945353267/1239148130679783424/1249699208773107733 | ||
| - https://github.com/goto-untrapped/Arai60/pull/25 | ||
|
|
||
| last_nonzero_indexとそこよりも右にある初めての非ゼロの要素を交換し続ける。考えてることは②と大体同じだが手順が違う。ごちゃついてるし②の方がいいだろう (書き方が悪いだけかもしれないが)。 | ||
|
|
||
| ```py | ||
| class Solution: | ||
| def moveZeroes(self, nums: List[int]) -> None: | ||
| next_nonzero_index = -1 | ||
| last_nonzero_index = 0 # any elements in nums[:last_nonzero_index] are not 0 | ||
| while True: | ||
| while last_nonzero_index < len(nums) and nums[last_nonzero_index] != 0: | ||
| last_nonzero_index += 1 | ||
| next_nonzero_index = max(next_nonzero_index, last_nonzero_index) | ||
| while next_nonzero_index < len(nums) and nums[next_nonzero_index] == 0: | ||
| next_nonzero_index += 1 | ||
|
Comment on lines
+114
to
+115
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. L111~112と!= と==のみが違う操作なので、関数化してもいいかもですね
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. 具体的にどう書くと良さそうでしょうか? 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. def find_leftmost_index_by_condition(nums, condition, index=0):
while index < len(nums):
if condition(nums[index]):
return index
index += 1
return index
find_leftmost_index_by_condition(nums, last_nonzero_index, lambda x: x != 0)みたいな感じですかね(思ったよりややこしかった)
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. class Solution:
def moveZeroes(self, nums: List[int]) -> None:
def find_leftmost_index_by(condition: Callable[[int], bool], start: int):
i = start
while i < len(nums) and not condition(nums[i]):
i += 1
return i
next_nonzero_index = -1
last_nonzero_index = 0 # any elements in nums[:last_nonzero_index] are not 0
while True:
last_nonzero_index = find_leftmost_index_by(lambda x: x == 0, last_nonzero_index)
next_nonzero_index = find_leftmost_index_by(lambda x: x != 0, max(next_nonzero_index, last_nonzero_index))
if next_nonzero_index == len(nums):
break
nums[last_nonzero_index], nums[next_nonzero_index] = nums[next_nonzero_index], nums[last_nonzero_index]
last_nonzero_index += 1自分なりに解釈してみました!PRのコードより良くなった気がします、ありがとうございます 🙏 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. おお、自分のやつより良くなってますね、ありがとうございます(後少し違っていた) 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. Generator を使って変なコードを書いてみました。 class Solution:
def moveZeroes(self, nums: List[int]) -> None:
eq0 = (i for i, x in enumerate(nums) if x == 0)
neq0 = (i for i, x in enumerate(nums) if x != 0)
try:
while 1:
pos0 = next(eq0)
while (posn0 := next(neq0)) < pos0:
pass
nums[pos0], nums[posn0] = nums[posn0], nums[pos0]
except:
passThere 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. try:
# なんかのエラーがある処理
except:
passでエラーを消せるのなるほどでした 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. このコードは遊びで書いているので、あまり参考にしないでください。 except ですべてを握りつぶすと、SIGINT つまり、キーボード入力の ^C も捕まえます。 StopIteration を捕まえるのが正しいでしょうね。 |
||
| if next_nonzero_index == len(nums): | ||
| break | ||
| nums[last_nonzero_index], nums[next_nonzero_index] = nums[next_nonzero_index], nums[last_nonzero_index] | ||
| last_nonzero_index += 1 | ||
| ``` | ||
|
|
||
| - https://discord.com/channels/1084280443945353267/1196472827457589338/1248287760255684722 | ||
| - https://github.com/Mike0121/LeetCode/pull/24 | ||
| - https://discord.com/channels/1084280443945353267/1225849404037009609/1246132840152699021 | ||
| - https://github.com/SuperHotDogCat/coding-interview/pull/25 | ||
| - https://discord.com/channels/1084280443945353267/1201211204547383386/1230566847058018344 | ||
| - https://github.com/shining-ai/leetcode/pull/54 | ||
| - https://discord.com/channels/1084280443945353267/1200089668901937312/1221011776188059668 | ||
| - https://github.com/hayashi-ay/leetcode/pull/58 | ||
| - https://discord.com/channels/1084280443945353267/1210494002277908491/1211045822012072006 | ||
|
|
||
|
|
||
| ## 3rd | ||
|
|
||
| ```py | ||
| class Solution: | ||
| def moveZeroes(self, nums: List[int]) -> None: | ||
| last_nonzero_index = 0 # any elements in nums[:last_nonzero_index] are not 0 | ||
| for i in range(len(nums)): | ||
| if nums[i] == 0: | ||
| continue | ||
| nums[last_nonzero_index], nums[i] = nums[i], nums[last_nonzero_index] | ||
|
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. もし時間にめちゃくちゃシビアな場合は、Step2の①の別解のスワップしない実装の方が速そうですね。(そんなシビアな事をpythonでするなという感じもありますが) この辺、3rdで最終的にこの方法を選んだ理由や考えが明示されていると良いかもしれないですね。
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.
なるほど、これから書いてみます。ありがとうございます
こうは考えていかなったですが、たしかにそうですね 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. まとめて 0 fill は、loop unrolling できたりするのでちょっと嬉しいこともあるでしょう。 |
||
| last_nonzero_index += 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.
C++ の std:remove() を思い出しました。