C++でソート済み配列内の要素の出現回数(頻度)を効率的に数える方法
整数型の要素からなるソート済み配列と、ある整数 num が与えられたとき、その要素が配列内に何回出現するか(=頻度)を求めるのが本記事の目的です。
入力例と出力例
- 入力:int arr[] = {1, 1, 1, 2, 3, 4}、num = 1
出力:ソート済み配列内の出現回数は − 3 - 入力:int arr[] = {2, 3, 4, 5, 5, 6, -7}、num = 5
出力:ソート済み配列内の出現回数は − 2 - 入力:int arr[] = {-1, 0, 1, 2, 3}、num = 7
出力:ソート済み配列内の出現回数は − 0
解法のアプローチ
この問題を解くには複数の方法があります。ここでは「素朴な方法(全走査)」と「二分探索を利用した効率的な方法」の2つを紹介します。
素朴なアプローチ(線形探索)
- 正負の整数を含む配列と、頻度を調べたい変数
numを宣言します。 - 配列のサイズを計算し、すべてのデータを関数に渡して処理します。
- 出現回数を格納する一時変数
countを宣言します。 - i を 0 から配列サイズまでループさせます。
- ループ内で
num == arr[i]であればcountを 1 増やします。 countを返して結果を出力します。
この方法の計算量は O(n) であり、配列全体を一度ずつ走査する必要があります。
効率的なアプローチ(lower_bound / upper_bound を活用)
配列がすでにソートされていることを活かせば、二分探索ベースの lower_bound と upper_bound を使うことで O(log n) の計算量で頻度を求められます。手順は以下の通りです。
- 配列と変数
numを宣言し、データを関数に渡します。 - ポインタ
firstをlower_bound(arr, arr+size, num)として設定します。lower_boundはnum以上の値が最初に現れる位置を返します。 first == (arr + size)または*first != numの場合は要素が存在しないため 0 を返します。- ポインタ
lastをupper_bound(first, arr+size, num)として設定します。upper_boundはnumより大きい値が最初に現れる位置を返します。 count = last - firstとすることで出現回数が得られます。countを返して結果を出力します。
サンプルコード(素朴なアプローチ)
#include <iostream>
using namespace std;
int frequency_count(int arr[], int num, int size){
int count = 0;
for(int i = 0; i < size; i++){
if(num == arr[i]){
count++;
}
}
return count;
}
int main(){
int arr[] = {1, 1, 1, 2, 3, 4};
int num = 1;
int size = sizeof(arr)/sizeof(arr[0]);
cout << "ソート済み配列内の出現回数: " << frequency_count(arr, num, size);
return 0;
}出力結果
ソート済み配列内の出現回数: 3
サンプルコード(効率的なアプローチ)
#include <bits/stdc++.h>
using namespace std;
int frequency_count(int arr[], int num, int size){
int *first = lower_bound(arr, arr + size, num);
if (first == (arr + size) || *first != num){
cout << "その要素は配列内に存在しません";
return 0;
}
int *last = upper_bound(first, arr + size, num);
int count = last - first;
return count;
}
int main(){
int arr[] = {1, 1, 1, 2, 3, 4};
int num = 1;
int size = sizeof(arr)/sizeof(arr[0]);
cout << "ソート済み配列内の出現回数: " << frequency_count(arr, num, size);
return 0;
}出力結果
ソート済み配列内の出現回数: 3
まとめ
線形探索による素朴な方法は実装が簡単ですが O(n) の計算量がかかります。一方、配列がソート済みである場合は lower_bound と upper_bound を組み合わせることで O(log n) で頻度を求められます。大規模なデータを扱う場合や頻繁に検索を行う場合には、後者の効率的なアプローチを選ぶことをおすすめします。
-
C++でソート済み配列の絶対値における異なる要素数を数える方法
配列(Array)とは、同じデータ型の要素を集めたデータ構造のことです。ソート済み配列とは、要素が昇順または降順に並べられた配列を指します。異なる要素数(distinct count)とは、配列内に重複して存在しない要素の数のことです。絶対値の異なる要素数(absolute distinct count)とは、各要素の絶対値(符号を無視した値)に着目したときの、異なる要素の数を意味します。この記事では、ソート済み配列における絶対値の異なる要素数を求めるプログラムを紹介します。つまり、配列の各要素の絶対値を考えた場合に、何種類の値が存在するかをカウントします。例を見てみましょう。入力 : [-3
-
C++で配列内の反転数(Inversion Count)を求めるプログラムの解説
「反転数(Inversion Count)」とは、配列を昇順にソートされた状態にするために必要な要素の入れ替え回数を表す指標です。配列がすでにソートされている場合、反転数は 0 となり、逆に配列が完全に逆順に並んでいる場合、反転数は最大値になります。この記事では、配列内の反転数を数えるC++プログラムを実際に作成しながら、その考え方と実装方法をわかりやすく解説します。反転数とは配列内の2つの要素 a[i] と a[j] について、i < j かつ a[i] > a[j] が成り立つとき、このペアを「反転(inversion)」と呼びます。配列全体に存在する反転ペアの総数が反転数です