# LeetCode Easy 374. Guess Number Higher or Lower
tags: leetcode
問題
我々は推測ゲームをしている. ゲームは次のようである.
私が$1$から$n$の数を1つ選ぶ. 私がどの数を選んだかを推測しなければならない.
間違って推測するごとに, その数が高いか低いかを教える.
予め定義された3つの可能性
(-1, 1, もしくは0)
を返すAPIguess(int num)
を呼び出しなさい-1 : 私の数の方が小さい
1 : 私の数の方が大きい
0 : おめでとう! 正解!
アイデア
$1$から$n$の範囲の数を当てるゲームで, 推測した数の大小から探し出します
単調性があるので二分探索が使えると考えられます
解法1(二分探索)
- 探索する範囲の左端と右端を決めます
mid = left + (right - left) / 2;
と定義して, ここからguess
APIの結果に応じて分岐させます- 結果が
ー1
のときは私の数の方が小さい
ので右端をmid - 1
にして左側を探索します - 結果が
1
のときは私の数の方が大きい
ので左端をmid + 1
にして右側を探索します - 結果が
0
のときは数を当てているのでこの値を返します
- 結果が
計算量
- 時間計算量
- 二分探索なので$O(\log n)$
- 空間計算量
- 必要と成る変数は定数個なので$O(n)$
C++
ところでなのですが, LeetCodeは関数の終わりに出力を書かないとコンパイラがエラーを吐きます
なので最後にreturn -1;
という無意味なコードを書く必要があるのです...
// Forward declaration of guess API. // @param num, your guess // @return -1 if my number is lower, 1 if my number is higher, otherwise return 0 int guess(int num); class Solution { public: int guessNumber(int n) { int left = 1, right = n; int res; while (left <= right) { int mid = left + (right - left) / 2; int guess_res = guess(mid); switch(guess_res) { case -1: right = mid - 1; break; case 1: left = mid + 1; break; default: res = mid; return res; } } return -1; } };
解法2(三分探索)
三分探索(Ternary Search)はその名の通り範囲を三分割して行う探索です
下の図のように探索範囲を三分割して, mid1
, mid2
の位置の値と探索する値から分岐をします
mid1
,mid2
の位置にある値が探索する値であればそれを返します- そうでなければ値に応じて探索する範囲を変える
計算量
- 時間計算量
- 毎回3等分しているので$O(\log_{3}n)$
- 空間計算量
- $O(n)$
C++
// Forward declaration of guess API. // @param num, your guess // @return -1 if my number is lower, 1 if my number is higher, otherwise return 0 int guess(int num); class Solution { public: int guessNumber(int n) { int left = 1, right = n; while (left <= right) { int mid1 = left + (right - left) / 3; int mid2 = right - (right - left) / 3; int res1 = guess(mid1); int res2 = guess(mid2); if (res1 == 0) return mid1; if (res2 == 0) return mid2; else if (res1 == -1) right = mid1 - 1; else if (res2 == 1) left = mid2 + 1; else { left = mid1 + 1; right = mid2 - 1; } } return -1; } };