C++
 Computer >> コンピューター >  >> プログラミング >> C++

【C++】サイズが不明なソート済み配列からターゲットを検索する方法

昇順にソートされた配列があるとします。この配列の中からターゲットとなる値を検索し、見つかった場合はそのインデックスを、存在しない場合は -1 を返す関数を定義する必要があります。

ここでのポイントは、配列のサイズが不明であるという点です。配列には ArrayReader インターフェースを通じてしかアクセスできず、ArrayReader.get(k) のように呼び出すことで、インデックス k の位置にある要素を取得できます。

例えば、入力が array = [-1,0,3,5,9,12]target = 9 の場合、9 は配列内に存在し、そのインデックスは 4 なので、出力は 4 となります。

解決のアプローチ

この問題は、次の手順で解くことができます。

  1. high := 1low := 0 で初期化します。

  2. reader.get(high) の値がターゲットより小さい間、以下を繰り返します(探索範囲を倍々に拡大)。

    • low := high

    • high = high * 2

  3. low <= high の間、通常の二分探索を実行します。

    • mid := low + (high - low) / 2

    • x := reader.get(mid)

    • x == target ならば、mid を返します。

    • x > target ならば、high := mid - 1

    • それ以外の場合は、low := mid + 1

  4. 見つからなければ -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) の効率でターゲットを検索できます。

  1. ソート済み連結リストをC++で高さ平衡な二分探索木(BST)に変換する方法

    問題の概要昇順にソートされた単方向連結リストが与えられたとき、それを高さ平衡な二分探索木(BST)に変換することを考えます。例えば、リストが [-10, -3, 0, 5, 9] の場合、生成される木は次のようになります。アルゴリズムのポイントこの問題を効率よく解く鍵は、リストの中央ノードを見つけて、それを木のルートにすることです。中央ノードより前の部分リストからは左部分木を、後ろの部分リストからは右部分木を再帰的に構築します。中央ノードの探索には、2つずつ進む高速ポインタ(fast)と1つずつ進む低速ポインタ(slow)を組み合わせる手法が便利です。手順リストが空の場合はNULLを返します。

  2. C++の配列減衰(Array Decay)とは?原因と防止方法を解説

    配列減衰(Array Decay)とは配列の型やサイズ(次元)の情報が失われる現象は、「配列減衰(array decay)」と呼ばれます。この現象は、配列を関数へポインタや値として渡したときに発生します。その際に関数へ渡されるのは配列の先頭アドレス、つまりポインタであるため、関数側では元の配列サイズを知ることができません。ここでは、C++における配列減衰の具体例を見てみましょう。サンプルコード#include<iostream> using namespace std; void DisplayValue(int *p) { cout << 値渡しした場合の配