数组作业问题

时间:2009-07-20 00:47:35

标签: arrays algorithm

您将获得一个整数介于1和1,000,000之间的数组。一个整数在数组中两次。你怎么决定哪一个?你能想出一种使用额外内存的方法吗?

Algo:

  • 解决方案1:
        
    1. 有一个哈希表   
    2. 遍历数组并将其元素存储在哈希表中   
    3. 一旦找到一个已经在哈希表中的元素,它就是dup元素
      优点:
      • 它在O(n)时间内运行,只有1次传递
      缺点:
      • 它使用O(n)额外内存
  • 溶液2:
    1. 使用合并排序(O(nlogn)时间)对数组进行排序
    2. 再次解析,如果你看到一个元素两次,你得到了dup。
      优点:
      • 它不使用额外的内存
      缺点:
      • 运行时间大于O(n)

你们能想到更好的解决方案吗?

9 个答案:

答案 0 :(得分:33)

这个问题有点含糊不清;当请求是“哪一个”时,是否意味着返回重复的,或者重复的位置?如果是前者,以下三种解决方案中的任何一种都可以使用;如果是后者,第一个是唯一有帮助的。

解决方案#1:假设数组是不可变的

构建位图;在迭代数组时设置 n 位。如果该位已设置,则表示您找到了重复项。它运行在线性时间,适用于任何大小的数组。

使用与数组中可能的值一样多的位来创建位图。在遍历数组时,检查数组中的 n 位。如果已设置,您已找到副本。如果不是,则设置它。 (这样做的逻辑可以在Bit arrays上的维基百科条目中的伪代码中看到,或者使用System.Collections.BitArray类。)

解决方案#2:假设数组是可变的

对数组进行排序,然后进行线性搜索,直到当前值等于之前的值。使用最少的记忆。用于改变排序算法以在比较操作期间检测重复并提前终止的加分点。

解决方案#3 :(假设数组长度= 1,000,001)

  1. 对数组中的所有整数求和。
  2. 从中,减去1到1,000,000的整数之和。
  3. 剩下的将是您的重复值。
  4. 如果你同时计算总和,这几乎不需要额外的记忆,可以一次完成。

    缺点是您需要完成整个循环才能找到答案。

    优点是简单,实际上它比其他解决方案运行得更快的可能性很高。

答案 1 :(得分:9)

假设1到1,000,000之间的所有数字都在数组中,所有1到1,000,000之间的数字之和为(1,000,000)*(1,000,000 + 1)/2 = 500,000 * 1,000,001 = 500,000,500,000

所以只需将数组中的所有数字相加,减去500,000,500,000,你就会得到两次出现的数字。

O(n)时间和O(1)记忆。

如果假设不正确,您可以尝试使用Bloom Filter - 它们可以比哈希表更紧凑地存储(因为它们只存储存在的事实),但他们确实存在误报的风险。但是,通过我们选择在布隆过滤器上花费多少内存,这种风险可能会受到限制。

然后我们可以使用布隆过滤器在O(n)时间内检测潜在的重复项,并在O(n)时间内检查每个候选项。

答案 2 :(得分:6)

这个python代码是modification of QuickSort

def findDuplicate(arr):
    orig_len = len(arr)
    if orig_len <= 1:
        return None
    pivot = arr.pop(0)
    greater = [i for i in arr if i > pivot]
    lesser = [i for i in arr if i < pivot]
    if len(greater) + len(lesser) != orig_len - 1:
        return pivot
    else:
        return findDuplicate(lesser) or findDuplicate(greater)

我认为它在O(n logn)中找到了重复。它使用堆栈上的额外内存,但我可以重写它只使用原始数据的一个副本,我相信:

def findDuplicate(arr):
    orig_len = len(arr)
    if orig_len <= 1:
        return None
    pivot = arr.pop(0)
    greater = [arr.pop(i) for i in reversed(range(len(arr))) if arr[i] > pivot]
    lesser = [arr.pop(i) for i in reversed(range(len(arr))) if arr[i] < pivot]
    if len(arr):
        return pivot
    else:
        return findDuplicate(lesser) or findDuplicate(greater)

产生更大较小的列表推导通过调用pop()来破坏原始文件。如果 arr 在从中删除更大 less 后不为空,则必须有重复且必须为 pivot

代码在排序数据上遇到通常的堆栈溢出问题,因此需要随机数据透视或对数据进行排队的迭代解决方案:

def findDuplicate(full):
    import copy
    q = [full]
    while len(q):
        arr = copy.copy(q.pop(0))
        orig_len = len(arr)
        if orig_len > 1:
            pivot = arr.pop(0)
            greater = [arr.pop(i) for i in reversed(range(len(arr))) if arr[i] > pivot]
            lesser = [arr.pop(i) for i in reversed(range(len(arr))) if arr[i] < pivot]
            if len(arr):
                return pivot
            else:
                q.append(greater)
                q.append(lesser)
    return None

但是,现在代码需要在循环顶部获取数据的深层副本,从而改变内存需求。

对计算机科学来说太过分了。天真算法在python中破坏了我的代码,可能是因为python的排序算法:

def findDuplicate(arr):
    arr = sorted(arr)
    prev = arr.pop(0)
    for element in arr:
        if element == prev:
            return prev
        else:
            prev = element
    return None

答案 3 :(得分:2)

我建议编写一个比较排序函数的实现,它会在找到dup后立即退出,导致没有额外的内存需求(取决于您选择的算法,显然),而不是对数组进行排序然后进行检查。最糟糕的情况是O(nlogn)时间(再次,取决于算法),而不是最佳(和平均,取决于...)情况O(nlogn)时间。

E.g。就地合并排序的实现。

http://en.wikipedia.org/wiki/Merge_sort

答案 4 :(得分:2)

提示:使用A XOR A == 0和0 XOR A == A的属性。

答案 5 :(得分:0)

作为解决方案的变体(2),您可以使用radix sort。没有额外的记忆,并将运行 线性时间。您可以争辩说时间也受到数字表示的大小的影响,但您已经为此给出了界限:基数排序在时间O(k n)中运行,其中k是您可以对每次通过排序的位数。这使得整个算法O(7n)用于排序加上O(n)来检查重复的数字 - 即O(8n)= O(n)。

优点:

  • 没有额外的记忆
  • O(n)的

缺点:

  • 需要八次O(n)次传球。

答案 6 :(得分:0)

找到所有重复的问题怎么样?这可以用不到的时间来完成 O(n nn)时间? (排序和扫描)(如果要恢复原始数组,请携带原始索引并在结束后重新排序,这可以在O(n)时间内完成)

答案 7 :(得分:0)

def singleton(array):
  return reduce(lambda x,y:x^y, array)

答案 8 :(得分:0)

通过对它们应该放置的位置进行排序来对整数进行排序。如果你得到“碰撞”而不是找到正确的数字。

空间复杂度O(1)(可以覆盖的空间相同) 时间复杂度小于O(n)因为你会在结束前统计发现collison。