查找数组中缺少的元素

时间:2018-02-16 16:53:13

标签: python algorithm data-structures binary-search

我有一个有趣的问题,给出了两个排序的数组:

a有n个元素,b有n-1个元素。

b包含除了一个元素之外的所有元素。

如何在O(log n)时间内找到该元素?

我试过这段代码:

def lostElements2(a, b):
    if len(a)<len(b):
        a, b = b, a

    l, r = 0, len(a)-1

    while l<r:
        m = l + (r-l)//2

        if a[m]==b[m]:
            l = m+1
        else:
            r = m - 1

    return a[r]


print(lostElements2([-1,0,4,5,7,9], [-1,0,4,5,9]))  

我不知道我应该在函数中返回什么,如果它是[l],[r]?

我得知函数内部的逻辑应该如何:如果两个数组的中间值匹配,则意味着,b直到中点与a相同,因此缺少的元素必须在mid的右边

但我无法创建最终解决方案,何时应该停止循环以及应该返回什么?它如何保证[l]或[r]确实是缺失的元素?

3 个答案:

答案 0 :(得分:4)

这个问题的原理很简单,细节很难。

您已安排数组a是较长的数组。好,这简化了生活。现在,您需要在a的值与a的值不同的第一个位置返回b的值。

现在您需要确保处理以下边缘情况。

  1. 不同的值是最后一个(即只有数组a具有值的位置。
  2. 不同的价值是第一个。 (对于这种情况,二进制搜索算法很容易搞砸。
  3. 有一个相同的运行。那是a = [1, 1, 2, 2, 2, 2, 3]b = [1, 2, 2, 2, 2, 3] - 当你落在中间时,价值相匹配的事实会误导你!
  4. 祝你好运!

答案 1 :(得分:4)

lr应该是l始终是列表相等的位置,而r始终是它们不同的位置。 IE浏览器。 a[l]==b[l]a[r]!=b[r]

代码中唯一的错误是将r更新为m-1而不是m。如果我们知道a[m]!=b[m],我们就可以安全地设置r=m。但是将其设置为m-1会冒a[r]==b[r],这会破坏算法。

def lostElements2(a, b):
    if len(a) < len(b):
        a, b = b, a
    if a[0] != b[0]:
        return a[0]

    l, r = 0, len(a)-1
    while l < r:
        m = l + (r-l)//2
        if a[m] == b[m]:
            l = m+1
        else:
            r = m # The only change
    return a[r]

(正如@btilly所指出的,如果我们允许重复的值,这个算法会失败。)

从@btilly编辑

要修复这个潜在的缺陷,如果值相等,我们会搜索具有相同值的范围。为此,我们以1号,2号,4号,8号等步长向前走,直到值切换,然后进行二分查找。并向后走同样的道路。现在寻找每个边缘的差异。

该搜索所需的工作是O(log(k)),其中k是重复值的长度。所以我们现在用搜索替换O(log(n))查找。如果在搜索的长度上存在上限K,则会产生总体运行时间。 O(log(n)log(K))。这使得最坏情况下的运行时间为O(log(n)^2)。如果K接近sqrt(n),则很容易实际遇到最糟糕的情况。

我在评论中声明,如果最多K个元素的重复次数超过K次,则运行时间为O(log(n)log(K))。进一步分析,这种说法是错误的。如果K = log(n)log(n)长度为sqrt(n)的游戏符合搜索的所有选项,那么您的运行时间为O(log(n)^2),而不是O(log(n)log(log(n)))

但是,如果最多log(K)个元素的重复次数超过K次,那么您的运行时间将为O(log(n)log(K))。对于大多数情况来说,哪个应该足够好。 : - )

答案 2 :(得分:3)

您的代码不处理缺少元素是索引m本身的情况。后面的if / else子句将始终移动缺少元素的边界,使其不包括m。

您可以通过添加额外的支票来解决此问题:

if a[m]==b[m]:
    l = m+1
elif m==0 or a[m-1]==b[m-1]:
    return a[m]
else:
    r = m - 1

另一种方法是存储m的最后一个值:

last_m = 0
...
else:
    last_m = m
    r = m - 1
...
return a[last_m]

这会导致它在上次检测到不匹配时返回。