【问题标题】:Given sorted integer array, write an algo based on divide and conquer for A[i]=i给定已排序的整数数组,写一个基于 A[i]=i 的分治算法
【发布时间】:2017-11-03 02:39:07
【问题描述】:

给定:排序的整数数组,整数都是不同的 - 没有重复。

问题:编写一个基于分治的算法(在运行时尽可能好),用于检查数组中是否存在 A[i]=i 的索引 i。 p>

好吧,我想到了二进制搜索,它是 O(logn) 运行时间复杂度。 还有比这更快的吗?

【问题讨论】:

  • 比这更快的只是 O(1),我认为您无法在那个时候找到解决问题的算法。所以,是的,二分搜索是您的最佳选择。
  • 二分搜索似乎是显而易见的选择。 O(loglogn) 也会比 O(logn) 快,但我看不出这是如何实现的。
  • 分而治之也意味着 O(logn),因为无论输入被除以什么常数 c,它仍然是 O(logn)。例如。如果 c = 4 会给出以 4 为底的对数,即 logn/log4,因为 log4 是一个常数,所以它仍然是 O(logn)。
  • @maraca 我明白了。谢谢马拉卡
  • 是否知道值的范围以及它们在该范围内的分布?

标签: arrays algorithm divide-and-conquer


【解决方案1】:

输入本身排序的要求是不够的。输入数组中没有两个相等值的附加要求是能够使用二进制搜索的必要条件。

如果 不是 是条件,则不能使用二分搜索,如下例所示(假设 0 是第一个索引):

    i:   0 1 2 3 4 5 6
        --------------
  A[i]: -1 0 x 4 y 6 7
               ^

通过二分搜索,您将获取中间元素并决定在它的哪一侧可能有iA[i]=i。问题是在上面的例子中,解决方案可能在中心的任一侧:如果x=2,那么解决方案在左边,当y=4,解决方案在右边。所以分而治之是行不通的。只有在确保输入没有重复值的情况下,分治算法才能工作。

除此之外,您可以立即排除第一个值大于 0 的输入数组,因为这样就无法在没有重复值的情况下实现A[i]=i。同样,最后一个值小于最后一个索引的输入数组没有iA[i]=i

这种考虑也适用于分而治之的过程。举个例子:

    i:   0 1 2 3 4 5 6 7 8
        -------------------
  A[i]: -4 0 1 2 5 6 7 8 10

首先验证末尾的两个值:它们不排除解决方案,因此采用索引 4 处的中间值。由于其值 (5) 大于索引 (4),因此可以忽略从索引 4 到 8 的范围。因此,在算法的下一次迭代中,将考虑索引 0 和 3(包括)之间的范围。

但是最右边的索引 (3) 的值小于 3(它是 2)。按照上面提到的规则,这意味着没有解决方案,所以算法可以停在那里:再多的划分是徒劳的。

所以这是一个 JavaScript 实现:

function hasSelfReference(arr) {
    let last = arr.length - 1;
    if (last < 0 || arr[0] > 0 || arr[last] < last) return false;

    let first = 0;
    while (first < last) {
        let mid = (first + last) >> 1;
        if (arr[mid] < mid) {
            first = mid + 1;
            if (arr[first] > first) return false;
        } else if (arr[mid] > mid) {
            last = mid - 1;
            if (arr[last] < last) return false;
        } else 
            return true;
    }
    return arr[first] === first; // arr[first] may be undefined: not a problem in JS
}

console.log(hasSelfReference([3, 4, 5, 6])); // false
console.log(hasSelfReference([-4, -2, 1, 2, 7])); // false
console.log(hasSelfReference([-4, -2, 1, 3, 7])); // true
console.log(hasSelfReference([])); // false
console.log(hasSelfReference([-4, 0, 1, 2, 5, 6, 7, 8, 10])); // false

与通常的分治算法一样,这具有 O(logn)(最坏情况)时间复杂度。

当数组有匹配索引时,那么迭代次数为logn,但是当没有匹配到,而且数组很大时,往往会出现提前退出循环。

【讨论】:

    【解决方案2】:

    让我们看一个例子:假设除 k 之外的所有索引 A[i]=i-1,其中 A[k]=k。仅在一个位置对数组进行采样并不能告诉您有关 k 的位置的任何信息(除非您碰巧碰巧碰到了 k)。

    因此,我不认为你可以获得比 O(n) 更好的最坏情况运行时。

    【讨论】:

    • 如果整数都不同怎么办?
    • @IlanAizelmanWS 我不明白你的评论。这有什么不同?顺便说一句。在我的示例中,所有整数都是不同的(最多有一个例外)。
    【解决方案3】:

    我认为最好使用二分查找

    1) 给你排序的整数

    2) 你需要一个分而治之的算法

    3) 二分查找的运行时间为O(logn),优于线性查找

    【讨论】:

    • 只是有个小缺点:这里不适用。
    • 如果整数都不同怎么办?
    • @IlanAizelmanWS 它将有效地处理不同的整数。不同的整数不会影响二分查找算法的时间复杂度或效率
    猜你喜欢
    • 2020-05-27
    • 2016-01-08
    • 1970-01-01
    • 1970-01-01
    • 2011-06-26
    • 2021-11-01
    • 2020-09-05
    • 2014-12-29
    • 2012-04-16
    相关资源
    最近更新 更多