【问题标题】:Find missing numbers in array in O(log(n)) time在 O(log(n)) 时间内查找数组中缺失的数字
【发布时间】:2020-01-22 22:33:23
【问题描述】:

我得到了两个数组 A 和 B,其中 A 完全用正整数填充,B 是 A,其中一些恒定数量的元素变成了零。我还得到了一个任意函数,它接受一个数组,并给出从 a 到 b 的 O(1) 中数组的总和,其中 a 和 b 是数组中的索引。

我应该设计一种算法,在 O(log(n)) 时间内将数组 B 转换为数组 A,但我很难理解这是如何实现的。

我想出了一个 O(n) 的解决方案,我只需将 A 的索引与 B 的索引进行比较。如果它们相同,我继续循环,如果它们不同,我更新B[i] 的值为 A[i]。我看不到改善这一点的方法,尤其是在数组未排序的情况下。

【问题讨论】:

  • 你得到的求和函数是 O(1) 吗?
  • 是的,我会补充的

标签: arrays algorithm performance complexity-theory


【解决方案1】:

让我们调用给定的函数sum。如前所述,您可以将其称为sum(array, i, j),它将返回array[i] + ... + array[j-1] 的总和。我假设该范围不包括 j 本身的索引,但如果包含它,推理是相同的。

我们也称​​kB中零的个数。

现在您可以使用二分查找来查找 B 中最左边的 0,方法是比较 sum(A, 0, i)sum(B, 0, i),同时根据二进制改变 i搜索方法。然后,当找到这些总和不相等的最低索引时,您就知道 B[i] 为零,并且在 O(logn) 时间内。

然后将相应的(非零)值从 A[i] 分配给 B[i]。然后重复这 k 次。由于k是常数,所以不影响时间复杂度:O(klogn)仍然是O(logn)k 是常量,例如 2。

这是一个带有示例输入的 JavaScript 实现,k=2

// Implementation of the hidden function, which (contrary to this implementation)
// should run in constant time
function sum(arr, i, j) {
    result = 0;
    while (i < j) {
        result += arr[i++];
    }
    return result;
}

// example
let a = [3,2,6,7,1,9,8,5];
let b = [3,2,0,7,1,9,0,5];
let n = a.length;
let k = 2;

for (let times = 0; times < k; times++) {
    // Apply binary search
    let low = 0;
    let high = n;
    while (low < high) {
        let mid = (low + high + 1) >> 1;
        if (sum(a, 0, mid) == sum(b, 0, mid)) {
            low = mid;
        } else {
            high = mid - 1;
        }
    }
    // Found index where zero is at:
    b[low] = a[low];
}

console.log(b);

k未知时

...那么它不是恒定的,而是可变的。然后时间复杂度变为O((k+1)logn),即O(klogn),并且循环必须继续进行,直到搜索不再找到零(在 (k+1)th 迭代):

// Implementation of the hidden function, which (contrary to this implementation)
// should run in constant time
function sum(arr, i, j) {
    result = 0;
    while (i < j) {
        result += arr[i++];
    }
    return result;
}

// example
let a = [3,2,6,7,1,9,8,5];
let b = [3,2,0,7,1,9,0,5];
let n = a.length;
// no definition of k here

while (true) {
    let low = 0;
    let high = n;
    while (low < high) {
        let mid = (low + high + 1) >> 1;
        if (sum(a, 0, mid) == sum(b, 0, mid)) {
            low = mid;
        } else {
            high = mid - 1;
        }
    }
    if (low >= n) break; // no zero found
    // Found index where zero is at:
    b[low] = a[low];
}
console.log(b);

【讨论】:

  • 感谢您的回答。不过,我有一个问题。您设置 k = 2 因为您知道 b 中有多少个零。如果你不知道 b 中有多少个零怎么办?找出零的数量不是需要线性时间吗?
  • 是的,但你的问题表明零的数量是恒定的,所以它是已知的。如果你不知道它,那么它是可变的。在这种情况下,它成为时间复杂度的一部分,for 循环必须添加一个检查以查看 A 和 B 的完全和是否相等,这就像找出是否仍然存在零一样。但是那个解决方案是O(klogn)。我在回答中添加了一个关于此的部分。
【解决方案2】:

二进制搜索零元素。 (提示:如果sum(A, lo, hi) ≠ sum(B, lo, hi) 则该范围至少包含一个零元素。如果您有这样的范围,则可以将其分成两半:如果前半部分包含零元素,则继续前半部分,否则继续下半场。当你到达一个元素范围时停止。

【讨论】:

  • 谢谢你的回答,你能简单解释一下为什么这是 O(log n) 吗?
  • @odenwright:在 N 达到 1 之前,你能将 N 分成多少次?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-23
  • 2023-01-16
  • 2014-12-28
相关资源
最近更新 更多