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

C++でソートされた0と1の配列から最初の「1」のインデックスを効率的に検索する方法

この問題では、ブール値(0と1のみ)で構成され、昇順にソートされた配列 bin[] が与えられます。求められるのは、配列内で最初に現れる「1」のインデックスを見つけることです。

問題の例

入力 : bin[] = {0, 0, 0, 1, 1}
出力 : 3

説明: ソート済みバイナリ配列の中で、最初に「1」が出現するのはインデックス3の位置です。

解法アプローチ1:線形探索(Linear Search)

最もシンプルな方法は線形探索です。配列を先頭(インデックス0)から末尾まで順番に走査し、「1」が最初に見つかった時点でそのインデックスを返します。もし配列内に「1」が存在しない場合は -1 を返します。

実装例

#include <iostream>
using namespace std;

int find1stOneInArray(int bin[], int n) {
    for (int i = 0; i < n; i++)
        if (bin[i] == 1)
            return i;
    return -1;
}

int main() {
    int bin[] = { 0, 0, 0, 1, 1, 1 };
    int n = sizeof(bin) / sizeof(bin[0]);
    cout << "配列内で最初に1が出現するインデックスは "
         << find1stOneInArray(bin, n);
    return 0;
}

出力結果

配列内で最初に1が出現するインデックスは 3

この方法の計算量は O(n) です。配列がソートされていることを活かしていないため、より効率的な手法が望まれます。

解法アプローチ2:二分探索(Binary Search)

配列がすでにソートされているため、二分探索を使うことで計算量を O(log n) まで削減できます。

探索の考え方は以下の通りです。

  • 中央の要素が「1」であり、かつその直前の要素が「0」(または先頭要素)であれば、それが最初の「1」なのでインデックスを返す。
  • 中央の要素が「1」だが条件を満たさない場合、答えは左側にあるので探索範囲を左半分に絞る。
  • 中央の要素が「0」の場合、答えは右側にあるので探索範囲を右半分に絞る。

実装例

#include <iostream>
using namespace std;

int find1stOneInArray(int bin[], int n) {
    int low = 0;
    int high = n - 1;
    int mid;
    while (low <= high) {
        mid = (low + high) / 2;
        if (bin[mid] == 1 && (mid == 0 || bin[mid - 1] == 0))
            return mid;
        else if (bin[mid] == 1)
            high = mid - 1;
        else
            low = mid + 1;
    }
    return -1;
}

int main() {
    int bin[] = { 0, 0, 0, 1, 1, 1, 1 };
    int n = sizeof(bin) / sizeof(bin[0]);
    cout << "配列内で最初に1が出現するインデックスは "
         << find1stOneInArray(bin, n);
    return 0;
}

出力結果

配列内で最初に1が出現するインデックスは 3

まとめ

手法時間計算量特徴
線形探索O(n)実装が簡単だが、大規模な配列では非効率
二分探索O(log n)ソート済み配列に最適で高速

配列がソートされているという前提条件があるため、実務上は二分探索を採用するのがベストプラクティスです。特に配列サイズが大きい場合、二分探索によるパフォーマンス向上は顕著になります。

  1. 配列の要素の積の最初の桁を求めるC++プログラム

    はじめにこの記事では、与えられた配列のすべての要素を掛け合わせた積の、最初の桁(最上位の桁)を求めるプログラムについて解説します。例として、次のような配列が与えられたとします。arr = {12, 5, 16}これらの要素の積は、12 × 5 × 16 = 960 となります。したがって、求める結果、つまり積の最初の桁は「9」になります。アルゴリズム変数 prod を 1 で初期化するループを使い、配列の各要素を順番に prod に掛けていくprod が 10 以上である間、prod を 10 で割り続ける残った一桁の値が、積の最初の桁となるサンプルコード#include <bits/s

  2. C++で配列を分割し、先頭部分を末尾に移動するプログラムの書き方

    この記事では、配列を指定した位置で分割し、分割した先頭部分を配列の末尾に移動させる方法を解説します。例として、配列の内容が {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} である場合を考えます。この配列を2つの部分に分割します。1つ目の部分はインデックス0から3まで(分割サイズ4)、2つ目の部分は残りです。先頭部分を末尾に追加すると、配列は {4, 5, 6, 7, 8, 9, 0, 1, 2, 3} のようになります。これは実質的に「左回転(left rotation)」と呼ばれる操作であり、先頭の要素を1つずつ取り出して末尾に移動する処理を、分割サイズ分だけ繰り返すことで実現