C++で指定した要素を削除した後にk個の最小値を見つける方法
問題の概要
この問題では、サイズnの配列arr[]、サイズmの配列del[]、そして整数kが与えられます。求められているのは、指定された要素を削除した後にk個の最小値を見つけることです。
具体的には、配列arr[]からdel[]に含まれるすべての要素を削除した後の、小さい方からk個の要素を出力する必要があります。同じ値が複数存在する場合は、最初に出現したインスタンスを削除するものとします。
例を使って問題を理解しましょう。
入力 : arr[] = {3, 5, 1, 7, 9, 2}, del[] = {1, 9, 3}, k = 2
出力 : 2, 5説明 −
del[]の要素を削除した後の配列arr[] : {5, 7, 2}
最小の2要素は 2 と 5 です。解決アプローチ1:ソートを使うシンプルな方法
最もシンプルな解決策は、まずarr[]からdel[]に存在するすべての要素を削除し、その後配列を昇順にソートして、先頭のk個の要素を出力するというものです。
削除処理にはO(m×n)、ソートにはO(n log n)の時間がかかるため、全体の計算量はO(n log n + m×n)となります。
実装例
この解決策の動作を示すプログラムです。
#include <bits/stdc++.h>
using namespace std;
void findKminElementDelArray(int arr[], int n, int del[], int m, int k){
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if(arr[j] == del[i]){
arr[j] = INT_MAX;
break;
}
}
}
sort(arr, arr + n);
for (int i = 0; i < k; ++i) {
cout<<arr[i]<<" ";
}
}
int main(){
int array[] = { 3, 5, 1, 7, 9, 2 };
int m = sizeof(array) / sizeof(array[0]);
int del[] = { 1, 9, 3 };
int n = sizeof(del) / sizeof(del[0]);
int k = 2;
cout<<k<<" smallest numbers after deleting the elements are ";
findKminElementDelArray(array, m, del, n, k);
return 0;
}出力
2 smallest numbers after deleting the elements are 2 5
削除対象の要素が見つかった場合、その値をINT_MAXに置き換えることで実質的に削除しています。その後ソートを行うことで、INT_MAXの要素は配列の末尾に移動するため、先頭のk個が求める最小値となります。
解決アプローチ2:ハッシュマップと最小ヒープを使う効率的な方法
より効率的なアプローチとして、ハッシュマップと最小ヒープ(min-heap)を組み合わせる方法があります。
手順は以下の通りです。
1. ハッシュマップを作成し、配列del[]のすべての要素を登録します。
2. 配列arr[]の要素を順に走査し、ハッシュマップに存在しない要素だけを最小ヒープに挿入します。
3. ヒープからk個の要素を取り出して出力します。
この方法では、削除対象の要素をハッシュマップでO(1)で判定できるため、削除処理がO(n + m)で完了し、全体の計算量はO(n + m + k log n)と大幅に改善されます。
実装例
この解決策の動作を示すプログラムです。
#include <bits/stdc++.h>
using namespace std;
void findKminElementDelArray(int arr[], int n, int del[], int m, int k){
unordered_map<int, int> deleteElement;
for (int i = 0; i < m; ++i) {
deleteElement[del[i]]++;
}
priority_queue<int, vector<int>, greater<int> > minHeap;
for (int i = 0; i < n; ++i) {
if (deleteElement.find(arr[i]) != deleteElement.end()) {
deleteElement[arr[i]]--;
if (deleteElement[arr[i]] == 0) deleteElement.erase(arr[i]);
}
else
minHeap.push(arr[i]);
}
for (int i = 0; i < k; ++i) {
cout<<minHeap.top()<<" ";
minHeap.pop();
}
}
int main(){
int array[] = { 3, 5, 1, 7, 9, 2 };
int m = sizeof(array) / sizeof(array[0]);
int del[] = { 1, 9, 3 }; int n = sizeof(del) / sizeof(del[0]);
int k = 2;
cout<<k<<" smallest numbers after deleting the elements are ";
findKminElementDelArray(array, m, del, n, k);
return 0;
}出力
2 smallest numbers after deleting the elements are 2 5
まとめ
配列から指定した要素を削除した後にk個の最小値を求める問題には、主に2つのアプローチがあります。
データサイズが小さい場合はシンプルなソートベースの方法で十分ですが、配列が大きい場合やパフォーマンスが重要な場面では、ハッシュマップと最小ヒープを組み合わせた方法が効率的です。del[]に同じ値が複数含まれるケースにも対応できるよう、ハッシュマップで出現回数を管理している点がポイントです。
-
【C++】指定されたインデックスのN個のフィボナッチ数のGCDを効率的に求める方法
本記事では、指定された複数のインデックスに対応するN個のフィボナッチ数の最大公約数(GCD)を、C++で効率的に求める方法を解説します。 フィボナッチ数列と問題の概要 まずおさらいとして、フィボナッチ数列は「0, 1, 1, 2, 3, 5, 8, 13, 21, 34, …」のように、直前の2つの項の和によって定義される数列です。インデックスは0から始まるため、0番目の要素は0、1番目の要素は1となります。 例えば、インデックス{2, 3, 4, 5}に対応するフィボナッチ数は{1, 2, 3, 5}であり、これらのGCDは1です。 鍵となる性質:GCD(Fibo(i), Fibo(j))
-
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! =