在列表中查找单个数字

时间:2008-08-29 20:03:58

标签: algorithm puzzle

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

因此,在整数列表中(让它作为数组),每个整数重复两次,除了一个。找到那个,什么是最好的算法。

11 个答案:

答案 0 :(得分:137)

最快(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标记为自身(或多或少)。唯一没有标记的是您的不重复。

答案 1 :(得分:18)

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

让我们调用唯一的数字a和b。首先考虑一切的异或,正如凯尔建议的那样。我们得到的是^ b。我们知道a ^ b!= 0,因为a!= b。选择a b的任意1位,并将其用作掩码 - 更详细地说:选择x作为2的幂,以便x&amp; (a ^ b)非零。

现在将列表拆分为两个子列表 - 一个子列表包含y和y&amp; x == 0的所有数字y,其余子列表位于另一个子列表中。顺便说一下,我们选择x,我们知道a和b在不同的桶中。我们也知道每对副本仍然在同一个桶中。因此,我们现在可以独立地将“XOR-em-all”技巧应用于每个桶,并发现a和b完全是什么。

的Bam

答案 2 :(得分:9)

O(N)时间,O(N)记忆

HT =哈希表

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

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

最后,HT中的项目是您要查找的项目。

注意(credit @Jared Updike):此系统将查找所有项目的奇数实例。


评论:我不知道人们如何投票给出能够提供NLogN性能的解决方案。宇宙是哪个“更好”? 我更加震惊的是你在NLogN解决方案中标记了已接受的答案......

我同意,如果要求内存保持不变,那么NLogN将是(到目前为止)最佳解决方案。

答案 3 :(得分:4)

如果数据集不遵守规则,凯尔的解决方案显然不会遇到问题。如果所有数字都是成对的,那么算法会得到零的结果,完全相同的值就好像零是单次出现的唯一值。

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

测试数据集可能会在内存或时间上以更昂贵的算法结束。

Csmba的解决方案确实显示了一些错误数据(没有或多于一个单独的出现值),但没有显示其他(四重组)。关于他的解决方案,根据HT的实现,存储器和/或时间多于O(n)。

如果我们无法确定输入集的正确性,排序和计数或使用哈希表计数出现,整数本身就是哈希键,这两种方法都是可行的。

答案 4 :(得分:1)

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

现在问题是找到“最好的”排序算法。有很多排序算法,每个都有它的优点和缺点,所以这是一个非常复杂的问题。 Wikipedia entry似乎是一个很好的信息来源。

答案 5 :(得分: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

答案 6 :(得分:0)

你需要用“最佳”来表达你的意思 - 对某些人而言,速度是最重要的,并且将答案限定为“最佳” - 对于其他人来说,如果解决方案更具可读性,他们可能会原谅几百毫秒。

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


那说:

迭代这些数字,对于每个数字搜索该数字的列表,当您达到只返回1作为搜索结果数量的数字时,您就完成了。

答案 7 :(得分:0)

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

它们是整数的事实并没有真正考虑因素,因为添加它们没有什么特别之处......是吗?

<强>问题

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

答案 8 :(得分:0)

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

答案 9 :(得分:0)

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

如果数字不受限制,则按位XOR需要时间O(k),其中k是位串的长度,并且XOR方法取O(nk)。现在,Radix sort将在时间O(nk)中对数组进行排序。

答案 10 :(得分:-1)

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

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