你有一个数组,其中每个数字都出现了奇数次(但不止一次)。只有一个数字出现了一次。如何找到只出现一次的数字?
e.g.: {1, 6, 3, 1, 1, 6, 6, 9, 3, 3, 3, 3}
答案是9。
我考虑使用哈希表,然后只计算出现次数为1的元素。这似乎很简单,但我没有利用每个其他元素重复了奇数次这一事实。是否有更好的方法?
你有一个数组,其中每个数字都出现了奇数次(但不止一次)。只有一个数字出现了一次。如何找到只出现一次的数字?
e.g.: {1, 6, 3, 1, 1, 6, 6, 9, 3, 3, 3, 3}
答案是9。
我考虑使用哈希表,然后只计算出现次数为1的元素。这似乎很简单,但我没有利用每个其他元素重复了奇数次这一事实。是否有更好的方法?
我相信你仍然可以使用异或的基本思想以巧妙的方式解决这个问题。
首先,让我们改变问题,使得一个数字出现一次,而所有其他数字都出现三次。
算法:
这里的A
是长度为n
的数组:
int ones = 0;
int twos = 0;
int not_threes, x;
for (int i=0; i<n; ++i) {
x = A[i];
twos |= ones & x;
ones ^= x;
not_threes = ~(ones & twos);
ones &= not_threes;
twos &= not_threes;
}
并且仅出现一次的元素存储在ones
中。这需要O(n)
时间和O(1)
空间。x^x=0
,因此所有成对的元素都会消失,留下孤立的元素。如果我们在这里尝试同样的策略,我们会得到不同元素的异或结果,这不是我们想要的。ones
是迄今为止仅出现一次的所有元素的异或
- twos
是迄今为止出现两次的所有元素的异或x
作为数组中的下一个元素时,有三种情况:x
第一次出现,则将其与ones
进行异或
- 如果这是x
第二次出现,则从ones
中取出它(再次进行异或)并将其与twos
进行异或
- 如果这是x
第三次出现,则将其从ones
和twos
中取出。ones
将是仅一个元素的异或,即不重复的孤立元素。我们需要查看5行代码才能看到为什么这行得通:在x = A[i]
之后的五行代码。x
第一次出现,则ones& x = ones
,因此twos
保持不变。行ones ^ = x;
对x
进行异或,如所述。因此,x
恰好位于ones
和twos
中的一个,因此在最后三行中,ones
和twos
都没有发生任何事情。x
第二次出现,则ones
已经有了x
(通过上面的解释),因此现在行twos |= ones & x;
使得twos
也有了x
。由于ones
有x
,所以行ones ^= x;
从ones
中除去了x
(因为x^x=0
)。再次,由于ones
和twos
中恰好有一个元素x
,最后三行不会对其中任何一个进行修改。x
第三次出现,则ones
没有x
,但twos
有。因此,第一行让twos
保持x
,第二行向ones
添加x
。现在,由于ones
和twos
都有x
,因此最后三行从两个元素中删除了x
2
。 - PengOneint findUnique(int A[], int size) {
// First we set up a bit vector and initialize it to 0.
int count[32];
for (int j=0;j<32;j++) {
count[j] = 0;
}
// Then we go through each number in the list.
for (int i=0;i<size;i++) {
int x = A[i];
// And for each number we go through its bits one by one.
for (int j=0;j<32;j++) {
// We add the bit to the total.
count[j] += x & 1;
// And then we take it modulo 3.
count[j] %= 3;
x >>= 1;
}
}
// Then we just have to reassemble the answer by putting together any
// bits which didn't appear a multiple of 3 times.
int answer = 0;
for (int j=31;j>=0;j--) {
answer <<= 1;
if (count[j] == 1) {
answer |= 1;
}
}
return answer;
}
我知道这个问题的背景是要找到一种高效或者性能良好的解决方案,但是我认为最简单易读的代码也是非常重要的,在大多数情况下它已经足够了。
那么这样怎么样:
var value = (new [] { 1, 6, 3, 1, 1, 6, 6, 9, 3, 3, 3, 3, })
.ToLookup(x => x)
.Where(xs => xs.Count() == 1)
.First()
.Key;
好老的LINQ。 :-)
Java,正确性100%,性能100%,任务得分100%
// you can also use imports, for example:
// import java.util.*;
// you can write to stdout for debugging purposes, e.g.
// System.out.println("this is a debug message");
import java.util.HashMap;
class Solution {
/*Simple solution
Will be using HashMap(for performance) as Array,
only Key set is needed.
Initially tried with ArryList but there was performance issue with
that so switch to HashMap.
Iterate over the given array and if item is there in key set
remove it(coz you found your pair) otherwise add as new Key.
After full Iteration only one key will be there in the Map that is
your solution.
In Short: If pair is found, remove it from Map's Key set,
Last Key will be your solution
*/
public int solution(int[] A) {
//Map but used as Array
final HashMap<Integer, Boolean> mapHave = new HashMap<>();
//Iterate over given Array
for (int nIdx = 0; nIdx < A.length; nIdx++) {
//Current Item
Integer nVal = A[nIdx];
//Try to remove the current item, if item does not exists will
//return null and if condition will be true
if (mapHave.remove(nVal) == null) {
//current item not found, add it
mapHave.put(nVal, Boolean.TRUE);
}
}
//There will be only one key remaining in the Map, that is
//your solution
return mapHave.keySet().iterator().next();
}
}
使用C#测试得分100%
using System;
using System.Collections.Generic;
// you can also use other imports, for example:
// using System.Collections.Generic;
// you can write to stdout for debugging purposes, e.g.
// Console.WriteLine("this is a debug message");
class Solution {
public int solution(int[] A) {
Dictionary<int, int> dic =new Dictionary<int, int>();
foreach(int i in A)
{
if (dic.ContainsKey(i))
{
dic[i]=dic[i]+1;
}
else
{
dic.Add(i, 1);
}
}
foreach(var d in dic)
{
if (d.Value%2==1)
{
return d.Key;
}
}
return -1;
}
}