C++で配列内の局所的な極値(極大値・極小値)を数える方法
要素数 n の配列 A が与えられたとします。配列の要素 A[i] が、両隣の要素(A[i-1] と A[i+1])のどちらよりも厳密に小さい場合、その要素は「局所的な極小値(local minimum)」と呼ばれます。逆に、両隣のどちらよりも厳密に大きい場合は「局所的な極大値(local maximum)」となります。
なお、配列の先頭要素 A[0] と末尾要素 A[n-1] は隣接要素が1つしか存在しないため、極大値にも極小値にもなりません。この記事では、与えられた配列に含まれる局所的な極値(極大値と極小値の合計)の個数を求める方法を解説します。
具体例
例として、入力が A = [1, 5, 2, 5] の場合を考えてみましょう。このとき出力は 2 となります。理由は以下の通りです。
- A[1] の値 5 は、隣接する 1 と 2 のどちらよりも大きいため局所的な極大値
- A[2] の値 2 は、隣接する 5 と 5 のどちらよりも小さいため局所的な極小値
アルゴリズムの手順
この問題を解くためには、以下の手順に従います。
- カウンタ sum を 0 で初期化します。
- 配列 A のサイズを n とします。
- i を 1 から n - 2 までループさせ、各要素について以下を判定します。
- A[i] が両隣より小さい、または両隣より大きい場合、sum を 1 増やします。
- 最後に sum を返します。
擬似コードで表すと次のようになります。
sum := 0
n := size of A
for initialize i := 1, when i < n - 1, update (increase i by 1), do:
if (A[i] < A[i - 1] and A[i] < A[i + 1]) or (A[i] > A[i + 1] and A[i] > A[i - 1]), then:
(increase sum by 1)
return sumC++での実装例
それでは、実際のC++コードを見てみましょう。理解を深めるために、以下の実装例を参照してください。
#include <bits/stdc++.h>
using namespace std;
int solve(vector<int> A){
int sum = 0;
int n = A.size();
for (int i = 1; i < n - 1; i++)
if ((A[i] < A[i - 1] && A[i] < A[i + 1]) || (A[i] > A[i + 1] && A[i] > A[i - 1]))
sum++;
return sum;
}
int main(){
vector<int> A = { 1, 5, 2, 5 };
cout << solve(A) << endl;
}入力
{ 1, 5, 2, 5 }出力
2
計算量について
このアルゴリズムは配列を一度だけ走査するため、時間計算量は O(n)、追加で必要なメモリは O(1) となります。配列のサイズが大きくなっても効率的に動作する、シンプルかつ実用的なアプローチです。
-
C++で指定された範囲内の階乗数の個数を数える方法
整数値が格納された変数startから変数endまでの範囲が与えられ、その範囲内に存在する階乗数の総数を求めるのがこの課題です。 階乗数とは ある数の階乗とは、その数から1ずつ減らしながら順に掛け合わせて計算される値です。「!」という記号で表され、0!、1!、2!、3!、5!のように書きます。なお、0!と1!はどちらも常に1となります。 例:2の階乗 = 2 × (2−1) = 2 × 1 = 2 3の階乗 = 3 × (3−1) × (2−1) = 3 × 2 × 1 = 6 具体例 入力 − start = 5, end = 600 出力 − 階乗数の個数は 3 説明 − 5〜60
-
C++でソート済みバイナリ配列に含まれる「1」の個数を数える方法
このチュートリアルでは、ソート済みバイナリ配列の中から「1」の個数を求めるプログラムについて解説します。扱うデータは、0と1のみで構成された配列です。課題は、この配列内に存在する「1」の個数を効率的に数えることです。アプローチのポイント配列が「1」が先頭側、「0」が末尾側という順序でソートされている場合、先頭から順に走査する線形探索では O(n) の時間がかかります。しかし、二分探索を活用すれば、O(log n) の時間計算量で「1」と「0」の境界位置を見つけられます。アルゴリズムの流れは以下のとおりです。探索範囲の中央要素 mid を確認するarr[mid] が 1 であり、かつ arr[m