【C++】サイズが不明なソート済み配列からターゲットを検索する方法
昇順にソートされた配列があるとします。この配列の中からターゲットとなる値を検索し、見つかった場合はそのインデックスを、存在しない場合は -1 を返す関数を定義する必要があります。
ここでのポイントは、配列のサイズが不明であるという点です。配列には ArrayReader インターフェースを通じてしかアクセスできず、ArrayReader.get(k) のように呼び出すことで、インデックス k の位置にある要素を取得できます。
例えば、入力が array = [-1,0,3,5,9,12]、target = 9 の場合、9 は配列内に存在し、そのインデックスは 4 なので、出力は 4 となります。
解決のアプローチ
この問題は、次の手順で解くことができます。
high := 1、low := 0で初期化します。reader.get(high)の値がターゲットより小さい間、以下を繰り返します(探索範囲を倍々に拡大)。low := highhigh = high * 2
low <= highの間、通常の二分探索を実行します。mid := low + (high - low) / 2x := reader.get(mid)x == targetならば、midを返します。x > targetならば、high := mid - 1それ以外の場合は、
low := mid + 1
見つからなければ
-1を返します。
アルゴリズムのポイント
配列のサイズがわからないため、まずは探索範囲の上限(high)を 1 から始めて 2 倍ずつ増やしていき、ターゲット以上の値が現れる位置を見つけます。この処理の計算量は O(log n) です。その後、確定した範囲 [low, high] 内で標準的な二分探索を行うことで、全体としても O(log n) の時間計算量で効率的に検索できます。
C++ 実装例
理解を深めるために、以下の実装例をご覧ください。
#include <bits/stdc++.h>
using namespace std;
class ArrayReader{
private:
vector<int> v;
public:
ArrayReader(vector<int> &v){
this->v = v;
}
int get(int i){
return v[i];
}
};
class Solution {
public:
int search(ArrayReader& reader, int target) {
int high = 1;
int low = 0;
// 探索範囲の上限を倍々に拡大
while (reader.get(high) < target) {
low = high;
high <<= 1;
}
// 二分探索
while (low <= high) {
int mid = low + (high - low) / 2;
int x = reader.get(mid);
if (x == target)
return mid;
if (x > target) {
high = mid - 1;
}
else
low = mid + 1;
}
return -1;
}
};
main(){
Solution ob;
vector<int> v = {-1,0,3,5,9,12};
ArrayReader reader(v);
cout<<(ob.search(reader, 9));
}入力例
{-1,0,3,5,9,12}, 9出力例
4
まとめ
サイズが不明なソート済み配列の検索では、「まず探索範囲を指数的に拡大して境界を見つけ、その後に二分探索を行う」という二段階のアプローチが有効です。これにより、配列の長さを直接知らなくても O(log n) の効率でターゲットを検索できます。
-
ソート済み連結リストをC++で高さ平衡な二分探索木(BST)に変換する方法
問題の概要昇順にソートされた単方向連結リストが与えられたとき、それを高さ平衡な二分探索木(BST)に変換することを考えます。例えば、リストが [-10, -3, 0, 5, 9] の場合、生成される木は次のようになります。アルゴリズムのポイントこの問題を効率よく解く鍵は、リストの中央ノードを見つけて、それを木のルートにすることです。中央ノードより前の部分リストからは左部分木を、後ろの部分リストからは右部分木を再帰的に構築します。中央ノードの探索には、2つずつ進む高速ポインタ(fast)と1つずつ進む低速ポインタ(slow)を組み合わせる手法が便利です。手順リストが空の場合はNULLを返します。
-
C++の配列減衰(Array Decay)とは?原因と防止方法を解説
配列減衰(Array Decay)とは配列の型やサイズ(次元)の情報が失われる現象は、「配列減衰(array decay)」と呼ばれます。この現象は、配列を関数へポインタや値として渡したときに発生します。その際に関数へ渡されるのは配列の先頭アドレス、つまりポインタであるため、関数側では元の配列サイズを知ることができません。ここでは、C++における配列減衰の具体例を見てみましょう。サンプルコード#include<iostream> using namespace std; void DisplayValue(int *p) { cout << 値渡しした場合の配