35. Search Insert Position#1
Conversation
| 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.
変数の宣言位置をここにして(pivot->int pivotにして)変数のスコープを狭めた方がいいと思います。
使わない場所でも変数を覚えておかないといけなくなるからです。
また元のコードでも0で初期化する必要はないと思います。
さらに言えば、僕はpivotよりmid(dle)の方が好きです。
pivotであるのは使われ方から分かっているので、それ以外の情報を伝えてほしいからです。
There was a problem hiding this comment.
レビューありがとうございます!
変数のスコープを厳密に気にしていませんでした。
そしてmidの方が役割が明確になりますね。
| while (left < right) { | ||
| pivot = left + (right - left) / 2; | ||
| if (target == nums[pivot]) return pivot; | ||
| else if (target < nums[pivot]) right = pivot; |
There was a problem hiding this comment.
参考までに今はぶら下がりよりブロック{}で囲むほうがスタイルとしては(多分)人気のようです。
フォーマットに関してはc言語だとclang-formatとかを導入してVSCode等で保存時に自動フォーマットを掛けるといいと思います。
philip82148/leetcode-swejp#9 (comment)
There was a problem hiding this comment.
ぶら下がりだと
if ...
if ...
else ...
の場合に最後のelseがどっちのifにかかっているのかわからないので避けるべきということですよね。今回はelse ifなのでわかりますが
There was a problem hiding this comment.
なるほど、そのような理由なのですね。ありがとうございます!
今後{}を使用しようと思います。
| while (left < right) { | ||
| pivot = left + (right - left) / 2; | ||
| if (target == nums[pivot]) return pivot; | ||
| else if (target < nums[pivot]) right = pivot; |
There was a problem hiding this comment.
僕は「<ループ毎に変わるのもの>は<変わらないもの>に一致するか」と考えたいので、これらの条件式でtargetを右辺にした方が読みやすいと感じます。
There was a problem hiding this comment.
おっしゃることも一理あると考えます。
自分が今回このように書いた理由は、小さいものを左で書いてあげる(>ではなく<を用いる)と、配列のソート順と一致するので、視覚的に分かりやすかったからです。
ただ、一般的には<変わらない閾値>を右に置くというのがセオリーですよね。
ここら辺のベストプラクティスはよくわかっていません。
| 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.
私は、ここの else 消しますね。上で return しているのでなくても同じです。
There was a problem hiding this comment.
確かに消しても問題なさそうですね。
一番上のif文を、下の二つの条件分岐から独立した処理にできるので、思考の処理を減らせる気がします。
| 配列のメモリは呼び出し元ですでに確保されている。 | ||
| *numsは配列の先頭の要素を取得、nums[pivot] == *(nums+pivot)。 | ||
|
|
||
| 他の人の回答を見てみると、閉区間、半開区間というか替えがあるらしい。<=と<の違いか。 |
There was a problem hiding this comment.
二分探索は様々なバリエーションがあります。どのような形でも読めるならば問題ないかと思います。
何を探しているのか、left, right が一体どういう条件を示す変数だと思っているのか、などが拾えるかということです。
結構よく話す内容なのでコメント集にも結構あります。
https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.c15qprmvxkc2
There was a problem hiding this comment.
コメント集ありがとうございます。
「left,rightの使い方や条件式」から伝わるニュアンスと、自分の実装方針が一致しているかということが大事だと理解できました。
とりあえず、自分がしっくりきている実装方法を一つ確立できました。
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
}There was a problem hiding this comment.
はい。
この場合、right とは、そこより右(含まない)がすべて True だと確信している場所、left とは、そこよりも左(含まない)がすべて False だと確信している場所ですね。
left, right の意味付けはコードごとに変わります。どういう順序で読み解くかに留意しながら色々と読んでみましょう。
|
PR のタイトルに問題名をいれていただけると助かります。 |
| if (nums[pivot] == target) return pivot; | ||
| else if (nums[pivot] > target) right = pivot; | ||
| else left = pivot + 1; |
There was a problem hiding this comment.
条件チェックが3つありますが、1つ目と、2&3つ目は意図が違うので、分離させたい気がします。
if nums[pivot] == target:
return pivot
if nums[pivot] > target:
right = pivot
else:
left = pivot + 1
Problem
https://leetcode.com/problems/search-insert-position/description/
Given a sorted array of distinct integers and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order.
You must write an algorithm with O(log n) runtime complexity.
Example 1:
Input: nums = [1,3,5,6], target = 5
Output: 2
Example 2:
Input: nums = [1,3,5,6], target = 2
Output: 1
Example 3:
Input: nums = [1,3,5,6], target = 7
Output: 4
Approach
2分探索で解きました。
Additional Notes