需要在数组中找到唯一的数字

时间:2015-12-12 21:12:25

标签: c++ algorithm implementation

这是问题(简而言之):

  

我们给出了一个具有N个自然数和val K的数组。我们需要   在数组中找到一次出现的数字   我的阵列中的任何其他数字显示为K次。

     

我们需要找到这个号码。

限制和规范

  

200.000&lt; = N <= 300.000
2 <= K <= 15
我的阵列中的任何数字是 0 ... 2 ^ 64-之间的自然数1

记忆&amp;执行时间限制:

  

记忆:0.5 Mb
时间:0.6秒

示例:

Type:

N K
<array vals>

10 3
1 3 5 7 5 1 3 1 5 3

就是这样。 我的主要问题是如何在我的数组中处理如此大的数字0 ... 2^64-1)。

我的想法听起来像那样(假设数字来自0 to 9):
- &GT;我从数组中计算出每个数字(数字)的出现次数,并将其标记为数字(数字)。

- &GT;我从0迭代到9,如果计算了数字(=我的数组中有这个数字)并且该数字的出现与K不同,我解决了问题。

但同样,我的数字来自0 to 2^64-1,我无法声明一个维数为2 ^ 64的数组!

你能给我一个想法吗?

4 个答案:

答案 0 :(得分:6)

您可以使用少于100个字节的额外空间在快速线性时间内完成此操作。

如果K是偶数,那么只需将所有元素混合在一起就可以完成。

考虑一下如何工作 - 考虑xor操作的一种方法是它将每个位视为一个单独的数字。它将它们加在一起并生成结果mod 2.任何乘以偶数的都是0 mod 2,因此只有在出现一次的数字中设置的位仍保持设置。

如果K不是偶数,那么你可以做同样的工作,但是修改K(或K因子 - 3或5)而不是mod 2.

假设:

int K,N;  //input values
uint64_t data[N]; //array of numbers

代码如下所示:

//initialize a counter for each bit in the result
int bitvals[64];
for (int bit=0; bit<64; ++bit)
{
    bitvals[bit]=0;
}

//count the number of times each bit occurs in the array
for(int i=0; i<N; ++i)
{
    uint64_t val=data[i];
    for(int bit=0; bit<64; ++bit)
    {
        if (val & (((uint64_t)1)<<bit))
            bitvals[bit]+=1;
    }
}

//only the bits in the number that occurs once are non-zero mod K
//make that number
uint64_t ret=0;
for(int bit=0; bit<64; ++bit)
{
    if (bitvals[bit]%K)
        ret |= ((uint64_t)1)<<bit;
}
return ret;    

额外信贷: 如果您愿意,可以使用位并行添加(JSF在此方向上的答案点)来优化此解决方案,但对于您需要的任何内容,这可能不是必需的。您可以使用5个64位整数来表示每个计数器的低5位。在将这些计数器扩展为位数数组之前,这些计数器最多可以累积31个输入值。累积每个单词看起来像这样:

   for (int i=0;i<5; i++)
   {
      uint64_t carry = parcounters[i]&val;
      parcounters[i]^=val;
      val=carry;
   }

答案 1 :(得分:2)

我认为输入已被读取但是太大而无法存储。

因此,当您阅读它时,计算每个64位的N位设置的次数。然后取其中每个计数mod k的余数,每个位的位置为零或一个给出该位位置的值。

如果您不介意编写大量繁琐的代码,可以编写六种不同的布尔模块化计数例程,并根据K的最低素因子选择其中一个:2,3,5,7,11或13

这避免了64位上的所有循环,并且对于2来说应该快6倍以上,并且对于最坏情况13可能仍然快8倍以上。

例如布尔计数mod 3可以用: 在循环a=b=0之前,然后为每个输入x

z = a | b;
a ^= x & ~b;
b ^= x & z;

然后在结尾处a

为5,您可以从a=b=c=0开始并使用:

b ^= x & a;
a ^= x & ~c;
c ^= x & ~(a|b);

7:

a ^= x & ~(c & b);
z = x & ~a;
c ^= b & z;
b ^= z;

享受11和13的乐趣。在所有情况下,最终答案都在a,没有额外的完成工作。缺少错误或输入错误,最后bc和(如果需要)d都将为零,因此这是一个简单的理智检查。

答案 2 :(得分:1)

也许我误解了这个问题,但这是一种解决问题的方法。

  1. 使用就地排序算法对数组进行排序。因为它是就地的,所以你不需要比初始数组更多的空间。这比地图更节省空间。
  2. 遍历数组,如果您找到一个没有重复的数字,那就是您的号码。
  3. 您甚至可以通过迭代每个Kth元素并查看前一个数字是否不同来优化步骤2。 (当目标数量是该组的最大或最小数量时,您仍然必须处理特殊情况)

答案 3 :(得分:-1)

首先对数组进行排序,然后对其进行迭代以获得答案。这是逻辑,唯一元素可以在任何标记为0,K,2K,3K,..,N-1的位置

#include <iostream>
#include <algorithm>

using namespace std;

unsigned long long uniqueNumber(vector<unsigned long long> &arr, int K) {
    sort(arr.begin(), arr.end());
    int i = 0;
    for(i = K-1;i < arr.size();i += K) {
        if(arr[i] != arr[i-K+1])
            return arr[i-K+1];
    }
    return arr[i-K+1];
}

int main()
{
    vector<unsigned long long> A{1, 3, 5, 7, 5, 1, 3, 1, 5, 3};
    cout<<uniqueNumber(A, 3)<<endl;
    return 0;
}