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) | ソート済み配列に最適で高速 |
配列がソートされているという前提条件があるため、実務上は二分探索を採用するのがベストプラクティスです。特に配列サイズが大きい場合、二分探索によるパフォーマンス向上は顕著になります。
-
配列の要素の積の最初の桁を求めるC++プログラム
はじめにこの記事では、与えられた配列のすべての要素を掛け合わせた積の、最初の桁(最上位の桁)を求めるプログラムについて解説します。例として、次のような配列が与えられたとします。arr = {12, 5, 16}これらの要素の積は、12 × 5 × 16 = 960 となります。したがって、求める結果、つまり積の最初の桁は「9」になります。アルゴリズム変数 prod を 1 で初期化するループを使い、配列の各要素を順番に prod に掛けていくprod が 10 以上である間、prod を 10 で割り続ける残った一桁の値が、積の最初の桁となるサンプルコード#include <bits/s
-
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つずつ取り出して末尾に移動する処理を、分割サイズ分だけ繰り返すことで実現