問題
小さいnums と、target をnums のtarget が
numsの値は 小さい 順に 並び、 同じ値は 2 回出てきません numsの長さは 0 以上 10000 以下、 値は どれも -10000 以上 10000 以下です - 時間は O(log n) で
解いてください
入力と出力の例
例 1
入力
nums = [-6, 0, 3, 8, 15, 21]
target = 8出力
3例 2
並びに
入力
nums = [2, 5, 9, 13]
target = 6出力
-1例 3
端の
入力
nums = [1, 4, 10]
target = 10出力
2面接での聞かれ方
面接では、
- 「ソート済みの
配列から、 目標の 数の インデックスを 返してください。 無ければ -1 です」 - 書き終えた
あとに 「なぜ O(log n) に なるのですか?」と 続けて 聞かれます - 「同じ
値が 何個も あったら、 どの 位置を 返しますか?」と、 問題文に 無い 場合を 聞かれる こともあります
二分探索は
まず 考える手順
段 1
まずはtarget と
段 2
並びがtarget より
段 3
探す範囲を left と right の 2 つのleft をright をleft <= right の
ここから解答です。先に自分で書いてから読むと、考え方が残ります。
解答
Python
Python
def binary_search(nums, target):
left = 0
right = len(nums) - 1
while left <= right:
both = left + right
mid = both // 2
if nums[mid] == target:
return mid
if nums[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1JavaScript
JavaScript
function binarySearch(
nums, target
) {
let left = 0;
let right = nums.length - 1;
while (left <= right) {
const mid = Math.floor(
(left + right) / 2
);
if (nums[mid] === target) {
return mid;
}
if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}nums がright が -1 からMath.floor で
計算量
- 時間は O(log n) です。
1 回見る たびに 範囲が およそ 半分に なるので、 10000 個でも 14 回ほどで 終わります - 使う
記憶は O(1) です。 持つのは left、right、midの 3 つの数だけです - 段 1 の
前から 見る 書き方は、 時間が O(n) です。 並びが 小さい 順という 手が かりを 使うと、 ここまで縮みます
よく ある間違い
- 止まる
条件を left < rightにする。 範囲に数が 1 つだけ 残った ときに 見ずに 抜けてしまい、 例 3 の 10 のような 端の 数を 見落とします left = midやright = midにする。 真ん中はもう 見終わっているので、 1 つずらさないと 範囲が 縮まない 場合が あり、 繰り返しが 止まらなくなります - 真ん中の
求め方で 桁が あふれる。 Python と JavaScript では 起きにくいですが、 整数の 大きさに 上限が ある 言語では、 leftとrightを足した 時点であふれる ことがあります。 その 言語では 「 leftに、2 つの 差の 半分を 足す」 書き方にします
面接での 話し方の例
書き始める
「並びが
小さい 順なので、 真ん中の 数と 比べれば 半分を 捨てられます。 範囲は 両端を 含む形で 持ち、 範囲に 数が 残っている 間だけ 繰り返します。 時間は O(log n)、 記憶は O(1) です。」
書き終えたら、
「例 2 の
[2, 5, 9, 13]で 6 を探します。 最初の 真ん中は 5 で、 6 より 小さいので 左の 端を 9 の 位置へ 動かします。 次の 真ん中は 9 で、 6 より 大きいので 右の 端を 5 の 位置へ 戻します。 ここで 左の 端が 右の 端を 越えたので、 範囲が 空に なり -1 を 返します。」
