-
Notifications
You must be signed in to change notification settings - Fork 0
35. Search Insert Position #39
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?
Conversation
// left and right are half open index such that [left, right) | ||
left := 0 | ||
right := len(nums) | ||
for { |
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.
このforはGO特有の書き方なのですね。
個人的には他の言語のwhileに合わせて条件を for left < right {...} として最後にreturn leftをしたくなります。
これはGO言語では一般的に使われるものでしょうか?(GOのことあまり知らずすみません)🙇♂️
気になった理由としては、44行目にのif文の条件を必ず満たすのか直感的にわかりづらかったためです。
調べたところ大丈夫そうですがコンパイラがエラーを吐かないのかなと思いました。
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.
おっしゃる通り自分も後でコードを見返した時に同じことを思ったのでstep3で「条件を for left < right {...} として最後にreturn left」するコードに変えました。Go言語の一般的な書き方かどうかというよりも、単に自分がわかりにくい書き方をしていました
無限ループをコンパイラがどう扱うのかについて自分も気になって色々試してみたところ、不思議な現象が起きました。まず、step1のコードのように無限ループを回してある条件を満たすとreturnするという場合にコンパイルエラーは生じません。さらに、下記コードでもコンパイルエラーは生じませんでした。int型を返す関数なのにreturn文がないよ、というエラーが出ることを期待したのですが、正常にコンパイルできてしまいました。"golang how compiler treats infinite loop with no return"などと調べてみても情報は見つからなかったです、、
func noReturnLoop() int {
i := 0
for {
i++
}
}
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.
@hroc135
説明いただきありがとうございました。step2以降は直感的にもわかりやすいと感じました。
自分の調べた限りでもGOだとコンパイラ可能ということしか見つけられませんでした。。。
Binary searchで躓いていて重点的に学習中なのですが各ステップのコメントすごく勉強になりました! |
nums[middle] == targetの時にすぐ答えを返すということをしなかったが、 | ||
今回のようにstrictly ascendingならそうした方が直感的だと思った | ||
- 停止性についての確認の仕方 | ||
- https://github.com/seal-azarashi/leetcode/pull/38#discussion_r1845409634 |
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.
そうですね。
ループごとに担当者がいると考えて、最終的な結果にミスがあったときに、どのループのときの責任なのか、というような考え方が分かりやすいでしょうか。
https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.c15qprmvxkc2
```Go | ||
func searchInsert(nums []int, target int) int { | ||
left := 0 | ||
right := len(nums) - 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.
答えが存在しうる範囲は[0, len(nums)]なので、最初にright=len(nums)-1としてバグが発生しないことを理解するのに時間がかかった
if nums[middle] == target { | ||
return middle | ||
} | ||
if nums[middle] < target { |
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.
この部分は左と右で対称の処理となるため、 continue せず if else で書いたほうが分かりやすくなるかもしれません。
- 初期状態として、配列の全ての要素が[left, right]にちょうど含まれている必要があるので、 | ||
left=0, right=len(nums)-1 | ||
5. 用いた区間の種類に対し、適切なループ不変条件を、理由を理解したうえで、設定できるか? | ||
- ループ終了時に、false,false],[true,trueとなって欲しい([はleft, ]はright)ので終了条件はleft > right |
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.
閉区間を使うのであれば、ループ終了時に false,false,[true],true と、一番左の true だけが区間の中に含まれていて欲しいと思いました。
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.
ご指摘ありがとうございます。配列が false,false,...,false の場合に困ると思って left と right が最終的に交差するように問題を設定したのですが、考え直してみて末尾にダミーの true を置いたら解決すると思い書き直してみました
- bool値からなる長さlen(nums)+1の配列を用意し、false: target未満、true: target以上又は末尾要素として false,...,false,true,...,true の一番左のtrueの位置を求める問題と捉える。末尾要素をいつでもtrueに設定することにより、全部falseからなる配列にならないようにする
- 位置を求めるにあたり答えの含まれる範囲を狭めていく
- 閉区間を用いる
- 初期値を[left, right] = [0, len(nums)] とする
- 終了条件は left == right なので不変条件は left < right
- middleを切り捨てでとる。nums[middle] < target なら left を middle+1 に更新。nums[middle] >= target なら right を middle に更新。middleは切り捨てなので left <= middle < right が成り立って毎ループ left -> middle+1 か right -> middle のどちらかが起こるので区間は少なくとも1狭まっていくので停止する
func searchInsert(nums []int, target int) int {
left := 0
right := len(nums)
for left < right {
middle := left + (right-left)/2
if nums[middle] < target {
left = middle + 1
} else {
right = middle
}
}
return left
}
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.
ありがとうございます。
ただ、ちゃぶ台返しになって大変申し訳ないのですが、この問題については、境界を求める問題として捉えると、求める位置が一番右側の要素の一つ右の場合でも、ストレートフォワードに答えが求まると思います。
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.
「境界を求める」と「一番左のtrueを求める」は同じ意味だと思っていたのですが違っていたのですね。
結局一番左のtrueを求める問題を解こうと思ったら閉区間を使っているつもりでも開区間を使った時のコードと同じになりました。
https://leetcode.com/problems/search-insert-position/description/