Array Homework Question
您将得到一个整数介于1和1000000之间的数组。数组中有两个整数。你如何确定哪一个?你能想出一个办法用一点额外的记忆来做吗?
Algo:
- 解决方案1:
- 有一个哈希表
- 遍历数组并将其元素存储在哈希表中
- 一旦找到一个已经在哈希表中的元素,它就是dup元素
- 它在O(N)时间内运行,只有1次通过
- 它使用O(N)额外内存
- 解决方案2:
- 使用合并排序(o(nlogn)时间)对数组排序
- 再次分析,如果看到一个元素两次,就得到了dup。
- 它不需要额外的内存
- 运行时间大于O(N)
- 赞成的意见:
欺骗:
- 赞成的意见:
欺骗:
你们能想出更好的解决办法吗?
这个问题有点模棱两可;当请求是"哪一个"时,是返回被复制的值,还是返回被复制的值的序列中的位置?如果是前者,下面三个解决方案中的任何一个都会起作用;如果是后者,第一个解决方案是唯一有帮助的解决方案。
解决方案1:假设数组是不可变的生成位图;在迭代数组时设置第n位。如果位已设置,则发现一个副本。它以线性时间运行,适用于任何大小的数组。
将使用数组中尽可能多的值创建位图。在迭代数组时,检查数组中的第n位。如果设置好了,您就找到了副本。如果不是,那么设置它。(这样做的逻辑可以在这个wikipedia条目中的伪代码中看到,也可以使用system.collections.bitarray类。)
解决方案2:假设数组是可变的对数组排序,然后进行线性搜索,直到当前值等于前一个值。使用的内存最少。在比较操作期间更改排序算法以检测重复项并提前终止的奖励点。
解决方案3:(假设数组长度=1000001)这几乎不需要额外的内存,如果同时计算和,可以一次完成。
缺点是你需要做整个循环来找到答案。
其优点是简单,而且事实上,它比其他解决方案运行得更快。
假设1到1000000之间的所有数字都在数组中,1到1000000之间的所有数字之和是
所以只要把数组中的所有数字加起来,减去500000,就可以得到两次出现的数字。
O(n)时间和O(1)内存。
如果假设不是真的,您可以尝试使用bloom过滤器-它们可以比哈希表更紧凑地存储(因为它们只存储存在的事实),但它们确实会带来误报的风险。但是,这种风险可以通过我们选择花多少内存在Bloom过滤器上来限制。
然后,我们可以使用Bloom过滤器在O(n)时间内检测潜在的重复项,并在O(n)时间内检查每个候选项。
此python代码是对quicksort的修改:
1 2 3 4 5 6 7 8 9 10 11 | 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)中发现了一个副本。它在堆栈上使用了额外的内存,但可以重写为只使用原始数据的一个副本,我相信:
1 2 3 4 5 6 7 8 9 10 11 | 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中移除大小部分后arr不是空的,那么必须有一个副本,并且必须是轴。
代码在排序后的数据上遇到了常见的堆栈溢出问题,因此需要一个随机透视或迭代解决方案来对数据进行排队:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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的排序算法:
1 2 3 4 5 6 7 8 9 | def findDuplicate(arr): arr = sorted(arr) prev = arr.pop(0) for element in arr: if element == prev: return prev else: prev = element return None |
。
提示:使用xor a==0和0 xor a==a的属性。
我建议不要对数组进行排序然后进行检查,而是编写一个比较排序函数的实现,该函数在发现dup后立即退出,这样就不需要额外的内存(显然取决于您选择的算法)和最坏的O(nlogn)时间(同样,取决于算法),而不是最佳的(和平均的)。e,取决于……)案例o(非登录)时间。
例如,就地合并排序的实现。
http://en.wikipedia.org/wiki/mergeu排序
通过按应该的位置对整数进行排序。如果你发现"碰撞",你会发现正确的数字。
空间复杂性o(1)(与可重写的空间相同)时间复杂度小于0(n),因为在到达终点之前,您会静态地找到collison。
1 2 | def singleton(array): return reduce(lambda x,y:x^y, array) |
找到所有副本的问题如何?这个能在以下时间内完成吗时间?(排序和扫描)(如果要还原原始数组,请携带原始索引并在结束后重新排序,这可以在o(n)时间内完成)
作为解决方案(2)的变体,可以使用基数排序。没有额外的内存,将运行线性时间。你可以说时间也会受到数字表示的大小的影响,但是你已经给出了限制:基数排序在时间o(k n)中运行,其中k是你可以在每次传递中对ar排序的位数。这使得整个排序算法O(7N)加上检查重复数字的O(N),即O(8N)=O(N)。
赞成的意见:
- 没有额外的内存
- o(n)
欺骗:
- 需要八个O(N)通行证。