-
Notifications
You must be signed in to change notification settings - Fork 0
704. Binary Search #13
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,135 @@ | ||
| ## step1 とりあえず解く | ||
| - 計算量 | ||
| - 時間:O(N) | ||
| - 空間:O(1) | ||
| - まず思いついた全探索 | ||
|
|
||
| ```java | ||
| class Solution { | ||
| public int search(int[] nums, int target) { | ||
| for (int i = 0; i < nums.length; i++) { | ||
| if (nums[i] == target) { | ||
| return i; | ||
| } | ||
| } | ||
| return -1; | ||
| } | ||
| } | ||
|
|
||
| ``` | ||
|
|
||
| ## step2 他の人の回答を見る | ||
| - 計算量 | ||
| - 時間:O(logN) | ||
| - 空間:O(1) | ||
| - ビルドインで提供されているメソッドを使う | ||
| - 「キーが見つかった場合にのみ戻り値が>= 0になることが保証される。」らしい | ||
| - https://docs.oracle.com/javase/jp/8/docs/api/java/util/Arrays.html#binarySearch-int:A-int- | ||
| - 面接でライブラリの使用が許されていないかも? | ||
|
|
||
| ```java | ||
| public class Solution { | ||
| public int search(int[] nums, int target) { | ||
| int index = Arrays.binarySearch(nums, target); | ||
| return index >= 0 ? index : -1; | ||
| } | ||
| } | ||
|
|
||
| ``` | ||
|
|
||
| - 計算量 | ||
| - 時間:O(logN) | ||
| - 空間:O(1) | ||
| - 一番よく見る二分探索 | ||
|
|
||
| ```java | ||
| public class Solution { | ||
| public int search(int[] nums, int target) { | ||
| int left = 0, right = nums.length - 1; | ||
|
|
||
| while (left <= right) { | ||
| int middle = 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. int middle = left + (right - left) / 2;で良いと思います。 |
||
| if (nums[middle] > target) { | ||
| right = middle - 1; | ||
| } else if (nums[middle] < target) { | ||
| left = middle + 1; | ||
| } else { | ||
| return middle; | ||
| } | ||
| } | ||
| return -1; | ||
| } | ||
| } | ||
|
|
||
| ``` | ||
|
|
||
| - 計算量 | ||
| - 時間:O(logN) | ||
| - 空間:O(1) | ||
| - Upper Bound | ||
| - targetを超える最初のindexを返す、そのためreturnで調整している | ||
|
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. うーん、まあ、分かっているような気がするんですが、ちょっと自信がもてないので
などを読んでおいてください。 |
||
|
|
||
| ```java | ||
| public class Solution { | ||
| public int search(int[] nums, int target) { | ||
| int left = 0, right = nums.length; | ||
|
|
||
| while (left < right) { | ||
| int middle = left + ((right - left) / 2); | ||
| if (nums[middle] > target) { | ||
| right = middle; | ||
| } else { | ||
| left = middle + 1; | ||
| } | ||
| } | ||
| return (left > 0 && nums[left - 1] == target) ? left - 1 : -1; | ||
|
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. 私の理解を深めるために質問を用意してくださりありがとうございます!
targetが存在する可能性がある最小のインデックスという認識です。
こちらはtargetが存在する可能性のある最大のインデックスの次のインデックスです。
こちらは
こちらはオーバーフローを防ぐためです。
背景として書いた通り、今回書いて見たかったのは単に配列の中にtargetがあるかどうか探索するだけでなくより多くの情報(具体的にはtargetより大きい値が出てくる最小のインデックス)をしれるコードをだったからです。
ループを抜けた段階でleftはtargetより大きい値が出てくる最小のインデックスを指します。
書き換えると私がStep3に書いたコードになるのかなと思っています。 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) の範囲の要素が区間に含まれていると仮定すると、 仮に left と right が要素と要素の間の境界を指し示しており、target 以下と target 超の要素の境目を探そうとしているのだと仮定します。すると、 [left, right] という区間には、探そうとしている境界が含まれ、 left == right となったときに、区間内には探そうとしている境界のみが残り、 配列の (境界のインデックス - 1) の位置に target が存在する可能性がある、と解釈できます。
言葉の使い方に違和感を感じました。区間には、配列の left から right - 1 までの要素が含まれる、でしょうか。 ただし、ループを抜けた段階で区間には要素が含まれなくなっているため、二分探索の考え方とコードが矛盾しているように感じました。
target より大きい値が存在する可能性がある最小のインデックス、でしょうか。
こちらも言葉の使い方に違和感を感じました。「
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 - 1 までの要素が含まれる」が正しくループ終了時にはleftとrightが同じ値になり境界が確定するという説明がただしかったですね。
こちらもご認識の通りです。
こちらも変なこと(ex: middleをtargetとする)書いてますね 🙇
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. @nodchip 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.
この説明を聞いた感じですと、二分探索の問題の設定があやふやなように感じました。今回の問題において、二分探索の問題の設定を、 target より大きい最小の要素の位置を特定する問題と考えていますか?それとも target 以下の要素と target 超の要素の境界を特定する問題と考えていますか?
この説明からも、区間の中に要素が含まれているのか、境界が含まれているのかが曖昧に感じました。
この文章からは、模範解答が存在し、それを答えることができておらず、減点された、という認識が見受けられます。元の質問には正解不正解はなく、首尾一貫した考え方・コーディングができているかを確認する意図がありました。 首尾一貫していたかいないかという点については、いなかったように思います。一番矛盾していた点は、 target より大きい最小の要素の位置を探そうとしていたにもかかわらず、ループが終了した時点で、区間の中に要素が残っていなかった点です。 その他、言葉の使い方に違和感を感じた点、説明が誤っていると感じた点が数点ありました。
ただ、人によって二分探索の考え方は異なっているようです。上記の考え方は、あくまで自分の考え方に沿って指摘・回答したものです。自分がどのように考えているかを認識・説明することができたうえで、他の人が書いた二分探索を理解できれば十分のようです。 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 は "targetが存在する可能性がある最小のインデックス" という表現は、なにかおかしいんですね。 ただ、その約束している内容としては可能性の話をするよりは具体的に否定している内容を書いたほうがいいでしょう。「0 から left - 1 (両端を含む)範囲すべてを開いたとして、それはすべて target 以下である。target と同じ値かもしれないが、target よりも大きい数字は存在しない。」ということですね。 |
||
| } | ||
| } | ||
| ``` | ||
|
|
||
| - 計算量 | ||
| - 時間:O(logN) | ||
| - 空間:O(1) | ||
| - Lower Bound | ||
| - target以上の最初の要素を探す | ||
|
|
||
| ```java | ||
| public class Solution { | ||
| public int search(int[] nums, int target) { | ||
| int left = 0, right = nums.length; | ||
|
|
||
| while (left < right) { | ||
| int middle = left + (right - left) / 2; | ||
| if (nums[middle] >= target) { | ||
| right = middle; | ||
| } else { | ||
| left = middle + 1; | ||
| } | ||
| } | ||
| return (left < nums.length && nums[left] == target) ? left : -1; | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## step3 3回とく | ||
| - 今回の問題には古典的な二分探索が最適だと感じたのでそのまま | ||
|
|
||
| ```java | ||
| class Solution { | ||
| public int search(int[] nums, int target) { | ||
| int left = 0, right = nums.length - 1; | ||
| while (left <= right) { | ||
| int middle = (left + right) / 2; | ||
| if (nums[middle] == target) { | ||
| return middle; | ||
| } else if (target > nums[middle]) { | ||
| left = middle + 1; | ||
| } else { | ||
| right = middle - 1; | ||
| } | ||
| } | ||
| return -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.
一番良く見るかどうか微妙に感じました。