C++で指定された値に最も近いk個の要素を検索する方法
いくつかの要素を含む配列 A があるとします。ここに、値 X と整数 k も与えられます。この課題は、配列 A の中から X に最も近い k 個の要素を見つけることです。なお、X が配列内に存在する場合は、その要素自体は出力に含めません。
例として、A = [12, 16, 22, 30, 35, 39, 42, 45, 48, 50, 53, 55, 56]、X = 35、k = 4 とすると、出力は「30, 39, 42, 45」になります。
解法の考え方:二分探索を活用する
この問題を効率的に解くには、二分探索(バイナリサーチ)の手法を利用します。二分探索によって「クロスオーバーポイント(X 以下の要素と X より大きい要素の境界となる位置)」を求めます。この境界位置さえ特定できれば、そこから左右へ同時にポインタを広げていくことで、O(k) の時間計算量で k 個の近接要素を出力できます。
アルゴリズムの流れ
- クロスオーバーポイントの探索: 二分探索を使い、X 以下で最大の要素のインデックスを求めます。
- 左右ポインタの設定: 見つかった位置を l、その右隣を r とします。もし arr[l] == x であれば、X 自身は出力対象外のため、l を1つ左へずらします。
- 距離の比較: x - arr[l] と arr[r] - x を比較し、より近い方の要素を出力して、その側のポインタを移動させます。
- 残りの補完処理: 片側の要素が尽きた場合は、もう片側から順番に必要な数だけ出力します。
C++での実装例
#include<iostream>
using namespace std;
int getCrossoverPoint(int arr[], int left, int right, int x) {
if (arr[right] <= x)
return right;
if (arr[left] > x)
return left;
int mid = (left + right)/2;
if(arr[mid] <= x && arr[mid+1] > x)
return mid;
if(arr[mid] < x)
return getCrossoverPoint(arr, mid+1, right, x);
return getCrossoverPoint(arr, left, mid - 1, x);
}
void findKClosestNumbers(int arr[], int x, int k, int n) {
int l = getCrossoverPoint(arr, 0, n-1, x);
int r = l+1;
int count = 0;
if (arr[l] == x) l--;
while (l >= 0 && r < n && count < k) {
if (x - arr[l] < arr[r] - x)
cout << arr[l--] << " ";
else
cout << arr[r++] << " ";
count++;
}
while (count < k && l >= 0){
cout << arr[l--] << " ";
count++;
}
while (count < k && r < n){
cout << arr[r++] << " ";
count++;
}
}
int main() {
int arr[] ={12, 16, 22, 30, 35, 39, 42, 45, 48, 50, 53, 55, 56};
int n = sizeof(arr)/sizeof(arr[0]);
int x = 35, k = 5;
findKClosestNumbers(arr, x, k, n);
}
実行結果
39 30 42 45 48
上記のコードでは x = 35、k = 5 として実行しているため、出力は5つの要素になっています。X = 35 は配列内に存在するため出力から除外され、それ以外の要素が X との距離が近い順に表示されていることが確認できます。
計算量について
- 時間計算量: 二分探索によるクロスオーバーポイントの探索に O(log n)、近接要素の出力に O(k)。全体としては非常に効率的です。
- 空間計算量: O(1)。追加の配列などは不要で、ポインタ変数のみで処理できます。
-
C++で配列内の各要素に最も近い大きい値を効率的に検索する方法
この記事では、配列内の各要素に対して「最も近い大きい値」を効率的に検索する方法を解説します。ある要素 x より大きい値が配列内に存在する場合、その中で最も小さい値(次に大きい要素)をその要素の答えとし、存在しない場合は -1 を出力します。例として、配列が {10, 5, 11, 10, 20, 12} の場合、結果は {11, 10, 12, 11, -1, 20} となります。最大値の 20 より大きい要素は配列内に存在しないため、20 に対しては -1 が出力されます。解決のアプローチこの問題は C++ STL の set(セット)を使うと簡単に解決できます。set は二分探索木をベース
-
C++で配列要素の階乗の最大公約数(GCD)を求める方法
N個の要素を持つ配列Aが与えられたとき、配列内のすべての要素の階乗の最大公約数(GCD)を求めることを考えます。例えば、配列の要素が {3, 4, 8, 6} の場合、各要素の階乗は 3! = 6、4! = 24、8! = 40320、6! = 720 となり、これらのGCDは 6 になります。解法のポイントここで重要な数学的な性質があります。2つの数のGCDとは、両方の数を割り切る最大の数のことです。階乗の場合、小さい数の階乗は必ず大きい数の階乗を割り切ることができます。つまり、2つの階乗のGCDは、小さい方の数の階乗そのものになります。例えば、3! と 5! のGCDを考えると、3! =