-
Notifications
You must be signed in to change notification settings - Fork 0
35. Search Insert Position #1
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,16 @@ | ||
| int searchInsert(int* nums, int numsSize, int target) { | ||
| int left = 0, right = numsSize - 1; | ||
| while (left <= right) { // 最後にrightとleftが交差して終わる形式。終了条件はright<left。 | ||
| int mid = left + (right - left) / 2; | ||
| if (nums[mid] >= target) { // [False,....,False,True,.....,True]の棒を考え、一番左のTrueが答えになる。 | ||
| right = mid - 1; | ||
| } | ||
| else { | ||
| left = mid + 1; | ||
| } | ||
| } | ||
| return left; | ||
| // 以下が終了状態。leftが答えになる。 | ||
| // [False,....,False,True,.....,True] | ||
| // right, left | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
|
|
||
| # step1で感じたこと | ||
|
|
||
| 30分かけて、エッジケースがうまくいかなかった(step1-draft)。 | ||
| 初稿では、left < rightの条件でwhile文を回したがうまくいかず。 | ||
| 時間計算量:O(log(n))、空間計算量:O(numsSize) | ||
| 命名は、tailよりleft、headよりrightの方が明確。 | ||
| 2分探索自体のロジックはわかるが、headとtail、pivot(2分探索のための軸)での条件分岐において、境界条件が悩ましい。 | ||
| while文で、left < rightにするか、left =< rightにするか。 | ||
| head,tailをpivot近辺に移せばいいのはわかるが、どうすればエラーが全てのケースを網羅できるか難しい。 | ||
| 最後のループまで答えが見つからなかった場合、どのように答えを与えるか迷った。 | ||
|
|
||
| # step2で感じたこと | ||
|
|
||
| 空間計算量は、定数なのでO(1)であった。 | ||
| c言語久々で忘れている、、 | ||
| int* numsでアドレスを渡しており、nums[pivot]で参照元のアドレスに格納された値を参照している。 | ||
| 配列のメモリは呼び出し元ですでに確保されている。 | ||
| *numsは配列の先頭の要素を取得、nums[pivot] == *(nums+pivot)。 | ||
|
|
||
| 他の人の回答を見てみると、閉区間、半開区間というか替えがあるらしい。<=と<の違いか。 | ||
|
|
||
| 正直、left <= rightで一回書いて仕舞えば、脳死で欠けてしまうので(頭使ってない)、left < rightで書いてみることにした。 | ||
|
|
||
| これが試作バージョン | ||
|
|
||
| ```c:step2.c | ||
| int searchInsert(int* nums, int numsSize, int target) { | ||
| int left = 0, right = numsSize - 1, pivot = 0; | ||
| while (left < right) { | ||
| pivot = left + (right - left) / 2; | ||
| if (nums[pivot] == target) return pivot; | ||
| else if (nums[pivot] > target) right = pivot; | ||
|
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. 私は、ここの else 消しますね。上で return しているのでなくても同じです。
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. 確かに消しても問題なさそうですね。 |
||
| else left = pivot + 1; | ||
| } | ||
| return left; | ||
| } | ||
| ``` | ||
|
|
||
| この場合の特徴は、else if (nums[pivot] > target) right = pivot;の部分であると思う。 | ||
| left == rightのループを許していない分、rightの移動をドラスティックにしすぎると、いきなりright==leftになりleftとtargetの大小比較ができなくなる。逆の条件分岐では、else left = pivot + 1;となり、閉区間手法と同じである。終盤で探索範囲のサイズが2になった場合に、left == pivotになるので、else left = pivot;だとループし続けてしまう。 | ||
|
|
||
| しかしながらこのプログラムだと、右端にtargetが挿入される場合(たとえば、[1,3,5,7]に対して、target==8だった場合)うまく機能しない。 | ||
| このプログラムは最終的に、left,right,pivotが一致する形で終了する。targetはnums[pivot]のみと比較される。 | ||
| よって、上記の具体例の場合、最後[5,7]に対してtarget(==8)の挿入位置を検討するが、pivot(==5)と比較を行うものの、right(==7)とは比較しない。よって、index3に入れるのかindex4に入れるのか、検討することができない。 | ||
|
|
||
| right = numSizeとすることで解決できることがわかった。 | ||
| 半開区間なので、rightを一つ多くとってあげると、右端まで全て検討してあげることができる。 | ||
|
|
||
| # step3で感じたこと | ||
|
|
||
| これくらいの規模のコードだと、深く考えずに脳死で書けてしまうので、閉区間、半開区間それぞれ2回ずつ書いた。 | ||
| 3回連続失敗せずに書くというのが、意外とストレスが溜まる作業だとわかった。 | ||
| 脳死で書いてしまうと、タイポが発生してやり直しになるので、頭を使って書くための制約なのだろうか。 | ||
| 結局、初回ということもあり、ここまでで5時間かかってしまった。。。 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| int searchInsert(int* nums, int numsSize, int target) { | ||
| int tail = 0; | ||
| int head = numsSize - 1; | ||
| int res = 0; | ||
|
|
||
| while (tail < head) { | ||
| int pivot = (tail + head) / 2 + 1; | ||
| if (nums[pivot] == target) { | ||
| res = pivot; | ||
| return res; | ||
| } | ||
| else if (nums[pivot] > target) { | ||
| head = pivot - 1; | ||
| res = pivot; | ||
| } | ||
| else { | ||
| tail = pivot; | ||
| res = pivot + 1; | ||
| } | ||
| } | ||
| return res; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| int searchInsert(int* nums, int numsSize, int target) { | ||
| int left = 0, right = numsSize - 1, pivot = 0; | ||
| while (left <= right) { | ||
| pivot = left + (right - left) / 2; | ||
| if (nums[pivot] == target) return pivot; | ||
| else if (nums[pivot] > target) right = pivot - 1; | ||
| else left = pivot + 1; | ||
| } | ||
| return left; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| int searchInsert(int* nums, int numsSize, int target) { | ||
| int left = 0, right = numsSize, pivot = 0; | ||
| while (left < right) { | ||
| pivot = left + (right - left) / 2; | ||
| if (nums[pivot] == target) return pivot; | ||
| else if (nums[pivot] > target) right = pivot; | ||
| else left = pivot + 1; | ||
| } | ||
| return left; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| int searchInsert(int* nums, int numsSize, int target) { | ||
| int left = 0, right = numsSize, pivot = 0; | ||
| while (left < right) { | ||
| pivot = left + (right - left) / 2; | ||
| if (nums[pivot] == target) return pivot; | ||
| else if (nums[pivot] > target) right = pivot; | ||
| else left = pivot + 1; | ||
|
Comment on lines
+5
to
+7
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. 条件チェックが3つありますが、1つ目と、2&3つ目は意図が違うので、分離させたい気がします。 if nums[pivot] == target:
return pivot
if nums[pivot] > target:
right = pivot
else:
left = pivot + 1 |
||
| } | ||
| return left; | ||
| } | ||
|
|
||
| int searchInsert(int* nums, int numsSize, int target) { | ||
| int left = 0, right = numsSize, pivot = 0; | ||
| while (left < right) { | ||
| pivot = left + (right - left) / 2; | ||
|
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. 変数の宣言位置をここにして(
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. レビューありがとうございます! |
||
| if (target == nums[pivot]) return pivot; | ||
| else if (target < nums[pivot]) right = pivot; | ||
|
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. ぶら下がりだと
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. 僕は「<ループ毎に変わるのもの>は<変わらないもの>に一致するか」と考えたいので、これらの条件式で
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. おっしゃることも一理あると考えます。 ただ、一般的には<変わらない閾値>を右に置くというのがセオリーですよね。 |
||
| else left = pivot + 1; | ||
| } | ||
| 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.
二分探索は様々なバリエーションがあります。どのような形でも読めるならば問題ないかと思います。
何を探しているのか、left, right が一体どういう条件を示す変数だと思っているのか、などが拾えるかということです。
結構よく話す内容なのでコメント集にも結構あります。
https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.c15qprmvxkc2
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.
コメント集ありがとうございます。
「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.
はい。
この場合、right とは、そこより右(含まない)がすべて True だと確信している場所、left とは、そこよりも左(含まない)がすべて False だと確信している場所ですね。
left, right の意味付けはコードごとに変わります。どういう順序で読み解くかに留意しながら色々と読んでみましょう。