查找数组的所有元素是不是不同的最快方法?
Posted
技术标签:
【中文标题】查找数组的所有元素是不是不同的最快方法?【英文标题】:fastest way to find if all the elements of an array are distinct?查找数组的所有元素是否不同的最快方法? 【发布时间】:2013-11-24 20:57:33 【问题描述】:我正在寻找一种更快的方法来查找元素数组是否仅包含不同的元素。最糟糕的做法是获取每个元素并将其与数组中的每个其他元素进行比较。下一个最好的方法是对列表进行排序,然后进行比较,但这仍然没有太大改善。有没有其他方法可以做到这一点?
【问题讨论】:
取决于语言及其内置类。但我认为你能做的最好的就是 O(N) 时间。 你的数组中有哪些元素?如果它们是数字,您可以使用数组和设置/测试位来查看之前是否已经设置了相同的数字。 有趣的事实:如果您知道元素的数量大于元素的范围,那么您就知道发生了碰撞。 排序并没有提高多少?让我们来看看。将每个元素与其他元素进行比较是 O(n^2),排序然后比较是 O(n log n)。当 n==1 百万时,您会发现排序和比较速度快得多。 最坏的情况不是 O(n^2 log n) 明显优于 O(n^2) 吗?因为元素是本地化的,所以您只需遍历排序数组一次即可将前一个元素的值与当前元素的值进行比较。如果它们相同,则您没有不同的元素。 【参考方案1】:蛮力:
蛮力(检查每个元素与其他元素)需要O(n<sup>2</sup>)
。
排序:
排序需要O(n log n)
,这通常被认为是相当不错的运行时间。
排序比下面(哈希表)方法具有优势,因为它可以就地完成(O(1)
额外空间),而下面需要O(n)
额外空间。
哈希表:
另一种方法是使用hash table。
对于每个项目:
检查该项目是否存在于哈希表中(如果存在,则所有项目都不是不同的)并且 将该项目插入哈希表由于插入和包含查询在哈希表上以预期的O(1)
运行,因此预计总运行时间为O(n)
,并且如上所述,O(n)
额外空间。
位数组:
如果元素都是某个给定范围内的整数,另一种选择是使用大小等于整数范围的bit array。
与哈希表方法所做的类似,对于每个元素,您将检查是否设置了适用位,然后设置它。
这需要O(m + n)
时间和O(m)
额外空间,其中m
是整数范围,n
是数组的大小(除非您考虑将数组分配为空闲,在这种情况下它只是需要O(n)
时间)。
【讨论】:
也许你想注意排序是 O(n log n) 而天真的比较所有对算法是 O(n^2) 这实际上是一个 lot更差。或者你不知道:) 好答案。还应注意哈希表需要 O(n) 额外空间。 @JimMischel 谢谢。为此添加了注释。 @rici 扩展了我的答案。【参考方案2】:创建一个红黑树,其中元素为键,出现次数为值。然后,您可以导航树。时间和空间复杂度为 O(n),其中 n 是元素的数量。使用红黑树的主要好处包括一致的性能和简单的内存管理——分布式环境的绝佳选择。欢迎观点。
【讨论】:
我找到了这种方式,但没有机会在这里更新。鉴于我只想要一个布尔值 true/false 来表示 distinct/non-distinct,我什至可以在遇到大于 1 的值时停止构造树。 时间复杂度不是 O(n)!红黑树插入是lg n,所以时间复杂度是n lg n 只是为了创建树【参考方案3】:另一种解决方案(仅从理论角度有趣):
我认为您可以调整Quickselect 算法。简而言之,该算法的运行方式与快速排序相同,但它仅根据某些选择的枢轴(分别小于和大于枢轴)将数组分成两组,因此省略了排序。它的平均案例性能是 O(n)。
我的想法是在每一步中寻找等于所选枢轴的元素。这样,只要有两个以上的元素,我们就会将枢轴与每个元素进行比较。如果我们找到了重复,我们就有了答案。否则,我们将问题分成两个相似的问题,但规模较小,然后在它们上运行算法。
免责声明:Quickselect 的最坏情况性能是 O(n^2)。因此,使用哈希表的效率更高时间。
但是,由于 Quickselect 是一个 in-place algorithm,它只需要恒定的内存开销,而不是哈希表的线性额外内存(现在并不重要)。
【讨论】:
您必须解释重复调用 Quickselect 比仅执行 Quicksort 更快。特别是您说,“在每一步中查找等于所选枢轴的元素。”什么是“每一步”? 我们将只调用一次类似 Quickselect 的算法(它没有 K 参数,由 Quickselect 使用)。它会比快速排序更快,因为它实际上不会对元素进行排序(这样它是 O(n))。 “每一步”我的意思是每次我们在递归函数的主体中并且我们正在选择一个枢轴。 但是你必须对列表中的每个项目都这样做,使得算法 O(n^2)。您可以通过在第一个数据透视后选择子集来将其优化为 O(n log n),但这样做需要额外的内存和开销,这将使其比快速排序慢。除非我完全误解了你的方法,在这种情况下,也许你应该发布一个实现。因为如果你可以在 O(n) 中做到这一点而没有额外的空间,那么你就证明了很多计算机科学理论是错误的。 你是对的。快速选择的优点是它只使用其中一个组递归地继续。在我的想法中,我必须在 both 组上调用该过程。因此,它失去了快速选择的优势。正如你所说,它可以在 O(n log n) 中实现,但这并没有为我的答案增加任何价值。【参考方案4】:这是 O(1) 空间复杂度的方法。我们的想法是,我们将只在数组的开头保留具有唯一元素的数组。 时间复杂度是 O(n*log(n)) 因为我们想避免空间使用,所以我们可以使用 python 的就地排序方法来处理列表。 它可能感觉像 C,但对我有用
a.sort()
i = 0
k = 0
while i < len(a) - 1:
if a[i] == a[i+1]:
j = i
while j < len(a) - 1 and a[j] == a[j+1]:
j += 1
if j < len(a) - 1:
a[k+1] = a[j+1]
i = j + 1
k += 1
else:
pass
else:
i += 1
k += 1
a = a[:k+1]
【讨论】:
以上是关于查找数组的所有元素是不是不同的最快方法?的主要内容,如果未能解决你的问题,请参考以下文章