C++でO(1)の追加メモリを使い、arr[i]をarr[arr[i]]になるように配列を並べ替える方法
正の整数型の配列 arr[] が与えられます。配列のサイズは任意ですが、すべての要素は 0 以上かつ配列のサイズ未満である必要があります。この課題の目的は、O(1) の追加メモリ領域しか使わずに、arr[i] が arr[arr[i]] となるように配列を再配置し、その最終結果を出力することです。
入出力シナリオの例
入力 − int arr[] = {0, 3, 2, 1, 5, 4}
出力 −
並べ替え前の配列: 0 3 2 1 5 4
O(1) の追加メモリで arr[i] を arr[arr[i]] に並べ替えた結果: 0 1 2 3 4 5
説明 − サイズ 6 の整数配列が与えられ、すべての要素は 6 未満です。ここで、arr[arr[0]] = 0、arr[arr[1]] = 1、arr[arr[2]] = 2、arr[arr[3]] = 3、arr[arr[4]] = 4、arr[arr[5]] = 5 となるように再配置します。したがって、再配置後の最終的な配列は 0 1 2 3 4 5 となります。
入力 − int arr[] = {1, 0}
出力 −
並べ替え前の配列: 1 0
O(1) の追加メモリで arr[i] を arr[arr[i]] に並べ替えた結果: 0 1
説明 − サイズ 2 の整数配列が与えられ、すべての要素は 2 未満です。ここで、arr[arr[0]] = 1、arr[arr[1]] = 0 となるように再配置します。したがって、再配置後の最終的な配列は 0 1 となります。
入力 − int arr[] = {1, 0, 2, 3}
出力 −
並べ替え前の配列: 1 0 2 3
O(1) の追加メモリで arr[i] を arr[arr[i]] に並べ替えた結果: 0 1 2 3
説明 − サイズ 4 の整数配列が与えられ、すべての要素は 4 未満です。ここで、arr[arr[0]] = 0、arr[arr[1]] = 1、arr[arr[2]] = 2、arr[arr[3]] = 3 となるように再配置します。したがって、再配置後の最終的な配列は 0 1 2 3 となります。
プログラムで使用しているアプローチ
- 整数型の要素からなる配列を入力として受け取り、配列のサイズを計算します。
- 並べ替え前の配列を出力し、関数 Rearrangement(arr, size) を呼び出します。
- 関数 Rearrangement(arr, size) の内部では次の処理を行います。
- i を 0 から size 未満までループさせます。ループ内では temp = arr[arr[i]] % size とし、arr[i] += temp * size を実行します。これにより、1 つの要素に「元の値」と「新しい値」の両方を一時的に格納できます。
- 続けて i を 0 から size 未満までループさせ、arr[i] = arr[i] / size を実行します。これにより、新しい値だけが取り出されます。
- 結果を出力します。
この手法のポイントは、配列のサイズ n を基数として利用することです。arr[i] % n で元の値を、arr[i] / n で新しく格納した値をそれぞれ復元できるため、補助配列を一切使わずに O(n) 時間・O(1) 空間で並べ替えが完了します。
コード例
#include <bits/stdc++.h>
using namespace std;
void Rearrangement(int arr[], int size){
for(int i=0; i < size; i++){
int temp = arr[arr[i]] % size;
arr[i] += temp * size;
}
for(int i = 0; i < size; i++){
arr[i] = arr[i] / size;
}
}
int main(){
// 配列の入力
int arr[] = {0, 3, 2, 1, 5, 4};
int size = sizeof(arr) / sizeof(arr[0]);
// 元の配列を出力
cout<<"Array before Arrangement: ";
for (int i = 0; i < size; i++){
cout << arr[i] << " ";
}
// 配列を並べ替える関数を呼び出す
Rearrangement(arr, size);
// 並べ替え後の配列を出力
cout<<"\nRearrangement of an array so that arr[i] becomes arr[arr[i]] with O(1) extra space is: ";
for(int i = 0; i < size; i++){
cout<< arr[i] << " ";
}
return 0;
}
出力
上記のコードを実行すると、次の出力が得られます。
Array before Arrangement: 0 3 2 1 5 4 Rearrangement of an array so that arr[i] becomes arr[arr[i]] with O(1) extra space is: 0 1 2 3 4 5
-
C++でO(1)の追加メモリを使って配列内の重複要素を効率的に検出する方法
問題の概要0からn-1までの範囲の数値が格納された配列を考えます。このとき、同じ数値は何度でも繰り返し現れる可能性があります。ここでの課題は、余分なメモリ(補助配列など)を使用せずに、重複している数値をすべて見つけることです。例えば、n = 7 の場合で、配列が [5, 2, 3, 5, 1, 6, 2, 3, 4, 5] のようになっているとします。このとき答えは 5, 2, 3 となります。アルゴリズムの考え方:符号マーキング法この問題をO(1)の追加空間で解く鍵となるのが「符号(正負)をマーキングとして利用する」テクニックです。配列の要素はすべて0からn-1の範囲内であるため、各値は必ず
-
C++で整数配列から最大の積を持つペアを見つける方法
配列Aにn個の異なる要素が含まれているとします。この配列Aから、積が最大になるペア(x, y)を見つける必要があります。配列には正の要素だけでなく、負の要素も含まれている可能性がある点に注意しましょう。例えば、配列が A = [-1, -4, -3, 0, 2, -5] の場合、(-4, -5) のペアが最大の積(20)を持つため、これが答えとなります。負の数同士を掛け合わせると正の数になるため、このようなケースが生じます。解決のアプローチこの問題を解くには、配列を一度走査しながら以下の4つの値を追跡します。positive_max:正の要素の最大値positive_second_max:正の