GallopSearch 合并:O(log(n)*log(i)) 而不是 O(n)
我继续在 cmets 中实施了灰胡子建议。主要是因为我需要此代码的高效关键任务版本。
- 代码使用了一个 GallopSearch,它是 O(log(i)),其中 i 是
与相关索引存在的当前索引的距离。
- 在驰骋搜索之后,代码使用 binarySearch
确定了正确的范围。由于疾驰将其限制为较小的
range 结果 binarySearch 也是 O(log(i))
- 疾驰和合并是向后执行的。这似乎不是
关键任务,但它允许就地合并阵列。如果其中之一
你的数组有足够的空间来存储结果值,你可以
只需将其用作合并数组和结果数组。在这种情况下,您必须在数组中指定有效范围。
- 在这种情况下不需要内存分配(在关键操作中节省大量资金)。它只是确保它不会也不能覆盖任何未处理的值(只能向后完成)。事实上,您对输入和结果使用相同的数组。它不会受到任何不良影响。
- 我一直使用 Integer.compare(),因此可以将其切换为其他用途。
- 我可能会犯一些错误而没有利用我之前证明过的信息。例如二进制搜索到两个值的范围,其中一个值已经被检查过。可能还有一种更好的方式来说明主循环,如果将它们按顺序组合成两个操作,则不需要翻转 c 值。因为你知道你每次都会做一个然后另一个。有一些修饰的空间。
这应该是最有效的方法,时间复杂度为 O(log(n)*log(i)) 而不是 O(n)。最坏情况的时间复杂度为 O(n)。如果你的数组是块状的并且有很长的值串在一起,这将使任何其他方法相形见绌,否则它会比它们更好。
它在合并数组的末尾有两个读取值,在结果数组中写入值。在找出哪个最终值较小之后,它会对该数组进行驰骋搜索。 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;
}