C++でソートされていない配列からXに最も近いk個の要素を検索する方法
ソートされていない整数型の配列A、基準となる値X、そして個数kが与えられたとします。この課題の目的は、配列Aの中からXに最も近いk個の要素を見つけ出すことです。ただし、Xが配列内に存在する場合、その要素は出力から除外されます。
例として、配列 A = [48, 50, 55, 30, 39, 35, 42, 45, 12, 16, 53, 22, 56]、X = 35、k = 4 が与えられた場合、出力は 30, 39, 42, 45 となります。
この問題を効率的に解くには、ヒープ(優先度付きキュー)データ構造を利用します。配列全体をソートするO(n log n)のアプローチと比べ、ヒープを使えばO(n log k)で処理でき、kが小さい場合に有利です。アルゴリズムの手順は以下の通りです。
- 最初のk個の要素について、Xとの絶対差をキーとして最大ヒープ(max-heap)を構築する
- k+1番目以降の各要素に対して、以下の処理を繰り返す
- 現在の要素とXの差(絶対値)を計算する
- その差がヒープのルート(現在の最大差)より大きい場合は、現在の要素を無視する
- そうでない場合は、ヒープのルートを削除した上で、現在の要素をヒープに挿入する
- すべての要素を処理し終えると、ヒープにはXに最も近いk個の要素が残る
差が同じ値になる要素が複数存在する可能性があるため、実装では差とともに配列のインデックスもペアでヒープに格納しています。
C++実装例
#include <iostream>
#include<queue>
using namespace std;
void findKClosestNumbers(int arr[], int n, int x, int k) {
priority_queue<pair<int, int> > priorityQ;
for (int i = 0; i < k; i++)
priorityQ.push({ abs(arr[i] - x), i });
for (int i = k; i < n; i++) {
int diff = abs(arr[i] - x);
if (diff > priorityQ.top().first)
continue;
priorityQ.pop();
priorityQ.push({ diff, i });
}
while (priorityQ.empty() == false) {
cout << arr[priorityQ.top().second] << " ";
priorityQ.pop();
}
}
int main() {
int arr[] = {48, 50, 55, 30, 39, 35, 42, 45, 12, 16, 53, 22, 56};
int x = 35, k = 5;
int n = sizeof(arr) / sizeof(arr[0]);
findKClosestNumbers(arr, n, x, k);
}実行結果
45 42 30 39 35
この実装では、k = 5 として実行したため、X = 35 に最も近い5つの要素 45, 42, 30, 39, 35 が出力されます。ヒープの操作は各要素につきO(log k)で済むため、大規模な配列でも効率的に動作する点がこの手法の大きな利点です。
-
【C++】配列内のすべての素数の積を求める方法
整数型配列 arr[] が与えられたとき、その配列に含まれるすべての素数を見つけ出し、それらの積を計算するのが本記事のテーマです。素数とは、1とその数自身でしか割り切れない正の整数のことです。たとえば、2、3、5、7、11などが素数に該当します。それでは、次の配列を例に解を求めてみましょう。入力: arr[] = { 11, 20, 31, 4, 5, 6, 70 }出力: 1705説明: 配列内の素数は 11、31、5 の3つであり、その積は 11 × 31 × 5 = 1705 となります。入力: arr[] = { 1, 2, 3, 4, 5, 6, 7 }出力: 210説明: 配列内の
-
【C++】n個の数値からなる集合Sの中で、中央値に最も近いk個の数を見つける方法
本記事では、n個の数値からなる集合Sが与えられたとき、その中央値(メジアン)に最も近いk個の数を求めるC++プログラムを紹介します。この問題は、まずクイックソートでデータを並べ替え、その後中央値を基準として左右にポインタを広げながら近い順に要素を選んでいくというアプローチで解くことができます。アルゴリズムの概要処理は大きく分けて「パーティション(分割)」「クイックソート」「中央値からの探索」の3段階で構成されます。1. partition関数:配列の分割配列a[]の末尾の値(high)をピボットとして扱い、それより小さい要素を左側に集めることで配列を分割します。Begin functi