GallopSearchマージ:O(n)ではなくO(log(n)* log(i))
私は先に進み、コメントに灰色ひげの提案を実装しました。ほとんどの場合、このコードの非常に効率的なミッションクリティカルバージョンが必要だったからです。
- コードはO(log(i))であるgallopSearchを使用します。ここで、iは関連するインデックスが存在する現在のインデックスからの距離です。
- コードは、ギャロップ検索が適切な範囲を識別した後に、binarySearchを使用します。ギャロップはこれをより小さい範囲に制限したため、結果のバイナリサーチもO(log(i))になります
- ギャロップとマージは逆方向に実行されます。これはミッションクリティカルとは思われませんが、アレイを適切にマージすることができます。配列の1つに結果値を格納するのに十分なスペースがある場合は、それをマージ配列および結果配列として使用できます。このような場合は、配列内の有効な範囲を指定する必要があります。
- その場合、メモリの割り当ては必要ありません(重要な操作の大幅な節約)。処理されていない値を上書きしないこと、および上書きできないことを確認するだけです(これは逆方向にのみ実行できます)。実際、入力と結果の両方に同じ配列を使用します。悪影響はありません。
- 私は一貫してInteger.compare()を使用したので、これを他の目的に切り替えることができます。
- 以前に証明した情報を少し間抜けて利用しなかった可能性があります。1つの値が既にチェックされている2つの値の範囲へのバイナリ検索など。また、メインループを記述するより良い方法があるかもしれません。cの値を順番に2つの演算に組み合わせれば、フリッピングc値は必要ありません。あなたが知っているので、あなたはいつもどちらか一方を毎回行うでしょう。少し磨きをかける余地があります。
これは、O(n)ではなくO(log(n)* log(i))の時間の複雑さで、これを行う最も効率的な方法である必要があります。そしてO(n)の最悪の場合の時間の複雑さ。配列が不揃いで値の長い文字列が一緒にある場合、これは他の方法で行うよりも小さくなります。それ以外の場合は、それらよりも優れています。
マージ配列の最後に2つの読み取り値があり、結果配列内に書き込み値があります。最終値がどちらであるかを見つけた後、その配列に対してギャロップ検索を実行します。1、2、4、8、16、32など。他の配列の読み取り値が大きい範囲が見つかった場合。それはその範囲をバイナリ検索します(範囲を半分にカットし、正しい半分を検索し、単一の値になるまで繰り返します)。次に、配列はそれらの値を書き込み位置にコピーします。コピーは、必然的に、どちらかの読み取り配列から同じ値を上書きできないように移動されます(つまり、書き込み配列と読み取り配列が同じであることを意味します)。次に、他の配列の新しい読み取り値よりも小さいことがわかっている他の配列に対して同じ操作を実行します。
static public int gallopSearch(int current, int[] array, int v) {
int d = 1;
int seek = current - d;
int prevIteration = seek;
while (seek > 0) {
if (Integer.compare(array[seek], v) <= 0) {
break;
}
prevIteration = seek;
d <<= 1;
seek = current - d;
if (seek < 0) {
seek = 0;
}
}
if (prevIteration != seek) {
seek = binarySearch(array, seek, prevIteration, v);
seek = seek >= 0 ? seek : ~seek;
}
return seek;
}
static public int binarySearch(int[] list, int fromIndex, int toIndex, int v) {
int low = fromIndex;
int high = toIndex - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
int midVal = list[mid];
int cmp = Integer.compare(midVal, v);
if (cmp < 0) {
low = mid + 1;
} else if (cmp > 0) {
high = mid - 1;
} else {
return mid;// key found
}
}
return -(low + 1);// key not found.
}
static public int[] sortedArrayMerge(int[] a, int[] b) {
return sortedArrayMerge(null, a, a.length, b, b.length);
}
static public int[] sortedArrayMerge(int[] results, int[] a, int aRead, int b[], int bRead) {
int write = aRead + bRead, length, gallopPos;
if ((results == null) || (results.length < write)) {
results = new int[write];
}
if (aRead > 0 && bRead > 0) {
int c = Integer.compare(a[aRead - 1], b[bRead - 1]);
while (aRead > 0 && bRead > 0) {
switch (c) {
default:
gallopPos = gallopSearch(aRead, a, b[bRead-1]);
length = (aRead - gallopPos);
write -= length;
aRead = gallopPos;
System.arraycopy(a, gallopPos--, results, write, length);
c = -1;
break;
case -1:
gallopPos = gallopSearch(bRead, b, a[aRead-1]);
length = (bRead - gallopPos);
write -= length;
bRead = gallopPos;
System.arraycopy(b, gallopPos--, results, write, length);
c = 1;
break;
}
}
}
if (bRead > 0) {
if (b != results) {
System.arraycopy(b, 0, results, 0, bRead);
}
} else if (aRead > 0) {
if (a != results) {
System.arraycopy(a, 0, results, 0, aRead);
}
}
return results;
}
これが最も効率的な方法です。
一部の回答には重複削除機能がありました。実際に各アイテムを比較する必要があるため、O(n)アルゴリズムが必要になります。だから、これはスタンドアロンであり、事後に適用されます。複数のエントリがある場合は、すべてを確認する必要がある場合は、複数のエントリを完全にギャロップすることはできませんが、重複がある場合は、ギャロップできます。
static public int removeDuplicates(int[] list, int size) {
int write = 1;
for (int read = 1; read < size; read++) {
if (list[read] == list[read - 1]) {
continue;
}
list[write++] = list[read];
}
return write;
}
更新:以前の回答、恐ろしいコードではありませんが、明らかに上記より劣っています。
別の不必要な超最適化。終了ビットだけでなく、開始ビットのarraycopyを呼び出します。データへのbinarySearchによるO(log(n))での非重複の紹介処理。O(log(n)+ n)はO(n)であり、場合によっては、特に、マージする配列間でオーバーラップがまったくない場合など、効果がかなり顕著になります。
private static int binarySearch(int[] array, int low, int high, int v) {
high = high - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
int midVal = array[mid];
if (midVal > v)
low = mid + 1;
else if (midVal < v)
high = mid - 1;
else
return mid; // key found
}
return low;//traditionally, -(low + 1); // key not found.
}
private static int[] sortedArrayMerge(int a[], int b[]) {
int result[] = new int[a.length + b.length];
int k, i = 0, j = 0;
if (a[0] > b[0]) {
k = i = binarySearch(b, 0, b.length, a[0]);
System.arraycopy(b, 0, result, 0, i);
} else {
k = j = binarySearch(a, 0, a.length, b[0]);
System.arraycopy(a, 0, result, 0, j);
}
while (i < a.length && j < b.length) {
result[k++] = (a[i] < b[j]) ? a[i++] : b[j++];
}
if (j < b.length) {
System.arraycopy(b, j, result, k, (b.length - j));
} else {
System.arraycopy(a, i, result, k, (a.length - i));
}
return result;
}