在列表中查找单个数字[重复]

Posted

技术标签:

【中文标题】在列表中查找单个数字[重复]【英文标题】:Finding a single number in a list [duplicate] 【发布时间】:2010-09-07 07:17:31 【问题描述】:

在所有其他数字恰好出现两次的列表中查找仅出现一次的数字的最佳算法是什么。

所以,在整数列表中(让我们把它当作一个数组),每个整数都重复两次,除了一个。要找到那个,最好的算法是什么。

【问题讨论】:

【参考方案1】:

在 Ruby 中的实现:

a = [1,2,3,4,123,1,2,.........]
t = a.length-1
for i in 0..t
   s = a.index(a[i])+1
   b = a[s..t]
   w = b.include?a[i]
   if w == false
       puts a[i]
   end
end

【讨论】:

【参考方案2】:

最快 (O(n)) 和最节省内存 (O(1)) 的方法是使用 XOR 操作。

在 C 中:

int arr[] = 3, 2, 5, 2, 1, 5, 3;

int num = 0, i;

for (i=0; i < 7; i++)
    num ^= arr[i];

printf("%i\n", num);

这会打印“1”,这是唯一出现一次的。

这是有效的,因为第一次点击一个数字时,它会用自身标记 num 变量,而第二次它会用自身取消标记 num(或多或少)。唯一未标记的是您的非重复项。

【讨论】:

这是“最佳”解决方案,只要您实际上可以异或这些项目。意思是,这取决于数据类型。如果项目是字符串,我不确定你是否可以这样做。当然,在这种情况下,它可以通过多一层抽象来解决...... 有一些方法可以通过对单个字符进行异或处理来对字符串进行异或处理——您只需要有一个与最大字符串一样大的临时变量。尝试对链表或其他一些复杂的数据结构进行异或是行不通的,但这个问题只是关于整数。 聪明的解决方案,但我认为负数可能会搞砸一点。您可能会在掩码中进行异或运算,从而完全抛弃其余掩码结果。 负数和正数一样是一个位域。 XOR 不在乎 @NickJohnson:您需要的不是哈希是“加密安全的”,而是它是“完美的”或“双向”或“唯一的”。您需要能够可靠地从哈希中返回对象。【参考方案3】:

Kyle 的解决方案显然无法捕捉数据集不遵守规则的情况。如果所有数字都成对出现,算法将给出零结果,与零完全相同的值将是唯一出现一次的值。

如果有多个单次出现值或三元组,结果也将是错误的。

测试数据集很可能最终会使用更昂贵的算法,无论是内存还是时间。

Csmba 的解决方案确实显示了一些错误数据(没有或不止一个出现值),但没有其他(四倍)。关于他的解决方案,根据 HT 的实现,内存和/或时间都超过 O(n)。

如果我们不能确定输入集的正确性,那么排序和计数或使用哈希表计数以整数本身作为哈希键的次数都是可行的。

【讨论】:

@malach Kyle 的提案完全解决了问题陈述中的内容。如果存在 O(n) 解决方案并且问题陈述没有提到数据错误的可能性,那么编写一个 O(nlogn) 解决方案来防止无效数据是没有意义的。无论如何,这里有一篇文章从信息论的角度用更多的话解释了解决方案:sysexpand.com/?path=exercises/number-appearing-once-in-array【参考方案4】:

排序方法和XOR方法具有相同的时间复杂度。如果假设两个字符串的按位异或是一个常数时间运算,则异或方法只有 O(n)。这相当于说数组中整数的大小由一个常数限定。在这种情况下,您可以使用基数排序以 O(n) 对数组进行排序。

如果数字没有界限,那么按位异或需要时间 O(k),其中 k 是位串的长度,异或方法需要 O(nk)。现在再次基数排序将在 O(nk) 时间内对数组进行排序。

【讨论】:

【参考方案5】:

顺便说一句,您可以扩展这个想法,以非常快速地在重复列表中找到两个唯一的数字。

让我们将唯一的数字称为 a 和 b。正如凯尔建议的那样,首先对所有内容进行异或。我们得到的是a^b。我们知道 a^b != 0,因为 a != b。选择 a^b 的任何 1 位,并将其用作掩码 - 更详细地说:选择 x 作为 2 的幂,以便 x & (a^b) 不为零。

现在将列表分成两个子列表 - 一个子列表包含所有数字 y 且 y&x == 0,其余的进入另一个子列表。通过我们选择 x 的方式,我们知道 a 和 b 在不同的桶中。我们还知道每对重复项仍然在同一个桶中。所以我们现在可以对每个桶独立地应用你旧的“XOR-em-all”技巧,并完全发现 a 和 b 是什么。

砰。

【讨论】:

喜欢这个。如果每个算法问题都带有这种扩展,如果将非常有帮助。【参考方案6】:

O(N) 时间,O(N) 内存

HT=哈希表

HT.clear() 按顺序浏览列表 对于您看到的每个项目

if(HT.Contains(item)) -> HT.Remove(item)
else
ht.add(item)

最后,HT 中的项目就是你要找的项目。

注意(信用@Jared Updike):这个系统会找到所有奇数的物品实例。


评论:我不明白人们如何投票支持给你 NLogN 性能的解决方案。在哪个宇宙中“更好”? 我更震惊你将接受的答案标记为 NLogN 解决方案......

但我确实同意,如果内存需要保持不变,那么 NLogN 将是(到目前为止)最好的解决方案。

【讨论】:

我现在没有看到一个被接受的答案,我想知道它是如何被接受的。顺便说一句,我会根据当时可用的答案标记一个接受的答案。此外,接受并不意味着最好:) 你的也不是很好:它使用 O(n) 内存。 看第一行,粗体:我明确地说这是 O(N) 时间,O(N) 内存,所以你没有批评我的建议,因为我还没有指出。 我认为您必须将 hash table 实现扩展为算法,因为问题发起者要求算法,而不是最佳数据结构适合。【参考方案7】:

取决于数字的大小/多样性。基数排序可能适用,这将在很大程度上减少 O(N log N) 解决方案的排序时间。

【讨论】:

【参考方案8】:

您可以简单地将集合中的元素放入哈希中,直到找到冲突为止。在 ruby​​ 中,这是一个单行代码。

def find_dupe(array)
  h=
  array.detect  |e| h[e]||(h[e]=true; false) 
end

所以,find_dupe([1,2,3,4,5,1]) 将返回 1。

不过,这实际上是一个常见的“技巧”面试问题。它通常是关于一个重复的连续整数列表。在这种情况下,面试官通常会寻找您使用 n 整数技巧的高斯和,例如n*(n+1)/2 从实际总和中减去。教科书的答案是这样的。

def find_dupe_for_consecutive_integers(array)
  n=array.size-1   # subtract one from array.size because of the dupe
  array.sum - n*(n+1)/2
end

【讨论】:

【参考方案9】:

似乎您能做的最好的事情就是遍历列表,对于每个项目,将其添加到“已见”项目列表中,或者如果它已经存在,则将其从“已见”项目中删除,最后是您的列表“看到”的项目将包括单数元素。这是关于时间的 O(n) 和关于空间的 n(在最坏的情况下,如果列表被排序会好得多)。

它们是整数的事实并没有真正考虑在内,因为将它们相加并没有什么特别的……是吗?

问题

我不明白为什么选择的答案在任何标准上都是“最好的”。 O(N*lgN) > O(N),它会更改列表(或者创建它的副本,这在空间和时间上仍然更昂贵)。我错过了什么吗?

【讨论】:

【参考方案10】:

我会说使用排序算法,然后通过排序列表查找数字是一种很好的方法。

现在的问题是找到“最好的”排序算法。排序算法有很多,每一种都有自己的长处和短处,所以这是一个相当复杂的问题。 Wikipedia entry 似乎是一个很好的信息来源。

【讨论】:

【参考方案11】:

您需要指定“最佳”的含义 - 对某些人来说,速度才是最重要的,并且会将答案限定为“最佳” - 对于其他人来说,如果解决方案更具可读性,他们可能会原谅几百毫秒。

“最佳”是主观的,除非您更具体。


也就是说:

遍历数字,为每个数字在列表中搜索该数字,当您到达搜索结果数量仅返回 1 的数字时,您就完成了。

【讨论】:

以上是关于在列表中查找单个数字[重复]的主要内容,如果未能解决你的问题,请参考以下文章

在 O(lg n) 中查找 Python 列表的唯一数字对中的单个数字

找出排序数组中重复数字的个数

python列表--查找集合中重复元素的个数

查找序列中缺少的数字[重复]

使用 LINQ 选择单个列表的所有唯一组合,不重复

使用 LINQ 选择单个列表的所有唯一组合,不重复