在数组

时间:2015-09-11 13:47:18

标签: arrays algorithm integer unique

我正在寻找一种算法来解决以下问题:我们给出了一个大小为n的整数数组,其中包含k(0

一个例子是输入[1, 2, 2, 4, 4, 2, 2, 3],其中1和3都是正确的输出。

最重要的是,算法应该在O(n)时间内运行,并且只需要额外的O(1)空间。

编辑:关于是否只有一个唯一整数或多个,存在一些混淆。我为此道歉。正确的问题是存在任意但固定的数量。我已经更新了上面的原始问题。

“但丁”。对于最多有两个这样的数字的情况给出了一个很好的答案。 This link还为三个人提供了解决方案。 “David Eisenstat”评论说,任何固定的k都可以做到。我很感激解决方案。

5 个答案:

答案 0 :(得分:10)

有一种标准算法可以使用XOR运算符解决此类问题:

时间复杂度= O(n)

空间复杂度= O(1)

假设您的输入数组只包含一个奇数次发生的元素,并且偶数发生次数,我们利用以下事实:

当应用xor时,任何具有偶数0和1以及任何顺序的表达式总是= 0。

那是

0^1^....... = 0 as long as number of 0 is even and number of 1 is even 

和0和1可以按任何顺序发生。

因为所有出现偶数次数的数字都会使其相应的位形成偶数1和0以及只有只发生一次的数字才会将其位遗漏我们采用数组的所有元素的xor,因为

0(from no's occuring even times)^1(from no occuring once) = 1 

0(from no's occuring even times)^0(from no occuring once) = 0

正如您所看到的那样,只保留了一次出现的数字。

这意味着当给出这样一个数组并且你获取所有元素的xor时,结果就是只出现一次的数字。

因此长度为n的数组的算法是:

 result = array[0]^array[1]^.....array[n-1] 

不同情景

正如OP提到的那样,输入也可以是一个数组,其中只有两个数字只出现一次而休息的次数是偶数次。

这是使用与上述相同的逻辑解决的,但差别很小。

算法理念

如果你取所有元素的xor,那么肯定发生偶数次元素的所有位将导致0,这意味着:

结果将仅在该位位置具有位1,其中两个数字的位仅发生一次不同。

我们将使用上述想法。

现在我们关注结果xor位为1(任何位为1)并使其休息为0.结果是一个数字,它允许我们区分这两个数字(必需的数字)。

因为位是1,这意味着它们在这个位置不同,这意味着一个在这个位置会有0而一个将有1.这意味着一个数字在被取和时导致0而一个不存在。

由于设置最右边的位非常容易,我们将结果xor设置为

 A = result & ~(result-1)

现在遍历数组一次,如果array [i]& A为0则将数字存储在变量number_1中

 number_1 = number_1^array[i]

,否则

 number_2 = number_2^array[i]

因为剩余的数字偶数次出现,它们的位会自动消失。

所以算法是

1.取所有元素的xor,称之为xor。

2.设置xor的最右边位并将其存储在B中。

3.执行以下操作:

number_1=0,number_2=0;
for(i = 0 to n-1)
{
 if(array[i] & B)
  number_1 = number_1^array[i];
 else
  number_2 = number_2^array[i];
}

number_1和number_2是必需的数字。

答案 1 :(得分:3)

问题标题是" 唯一整数",但问题正文说可能有多个唯一元素。

如果实际上只有一个非重复:将所有元素放在一起。重复项全部取消,因为它们成对出现(或更高的2的倍数),因此结果是唯一的整数。

请参阅Dante的答案,了解这个可以处理两个独特元素的想法的扩展。它不能概括为更多。

对于k个唯一元素,我们可以使用k累加器来跟踪sum(a[i]**k)。即a [i],a [i] 2 等。这可能仅适用于Faster algorithm to find unique element between two arrays?,而不是重复项都在一个数组中的情况。 IDK,如果正方形,立方体等xor可用于解决问题。

答案 2 :(得分:3)

这是一个拉斯维加斯算法,给定k,奇数次出现的元素的确切数量,在预期时间O(nk)中报告所有元素(读取:当k为O时的线性时间(1))和空间O(1)字,假设“给我一个统一的随机字”和“给我这个字中设置的1位数(popcount)”是恒定时间操作。我很确定我不是第一个提出这种算法的人(我甚至不确定我是否记得所有的改进),但是我已经达到了耐心的极限找到它。

中心技术称为随机限制。基本上我们所做的是按值随机过滤输入,希望我们保留一个奇数计数元素。我们将经典的XOR算法应用于过滤后的数组并检查结果;如果它成功了,那么我们假装将它添加到数组中,使其均匀计数。重复,直到找到所有k个元素。

过滤过程是这样的。将每个输入字 x 视为长度为w的二进制向量(与w无关)。通过ceil(1 + lg k)和长度为ceil(1 + lg k)的随机二进制向量 b 计算随机二进制矩阵 A 。我们通过保留那些 x 来过滤输入,使得 Ax = b ,其中左侧是矩阵乘法模2.在实现中, A 表示为ceil(1 + lg k)向量a1, a2, ...。我们将 Ax 的位计算为popcount(a1 ^ x), popcount(a2 ^ x), ...。 (这很方便,因为我们可以将比较与 b 短路,从而减少运行时间因素lg k。)

分析表明,在给定的传递中,我们以恒定的概率管理单个奇数元素之一。首先请注意,对于某些固定的 x Ax = b 的概率为2 -ceil(1 + lg k) =Θ(1 / K)。鉴于 Ax = b ,对于所有 y≠x Ay = b 的概率小于2 -ceil(1) + lg k)。因此,伴随 x 的预期元素数量小于1/2,因此概率大于1/2, x 在滤波输入中是唯一的。对所有k个奇数计数元素求和(这些事件是不相交的),概率为Θ(1)。

这是k = 3的确定性线性时间算法。让奇数元素为a, b, c。累计数组的XOR,即s = a ^ b ^ c。对于每个位i,请注意,如果a[i] == b[i] == c[i],则s[i] == a[i] == b[i] == c[i]。再次通过数组,累加s ^ x中设置的最低位的XOR。偶数元素再没有贡献。两个奇数计数元素贡献相同的位并相互抵消。因此,在XOR中设置的最低位是奇数计数元素中的一个与s不同的位置。我们可以使用上面的限制方法找到它,然后用k = 2方法找到其他方法。

答案 3 :(得分:1)

跟踪每个元素的计数,并仅返回计数为1的元素。这可以使用哈希映射来完成。下面的示例使用哈希集跟踪结果,同时它仍在构建计数图。仍然是O(n)但效率较低,但我认为它更有启发性。

使用jsfiddle http://jsfiddle.net/nmckchsa/

的Javascript
function findUnique(arr) {
    var uniq = new Map();
    var result = new Set();
    // iterate through array
    for(var i=0; i<arr.length; i++) {
        var v = arr[i];
        // add value to map that contains counts
        if(uniq.has(v)) {
            uniq.set(v, uniq.get(v) + 1);
            // count is greater than 1 remove from set
            result.delete(v);
        } else {
            uniq.set(v, 1);
            // add a possibly uniq value to the set
            result.add(v);
        }
    }
    // set to array O(n)
    var a = [], x = 0;
    result.forEach(function(v) { a[x++] = v; });
    return a;
}
alert(findUnique([1,2,3,0,1,2,3,1,2,3,5,4,4]));

编辑由于非uniq数字出现偶数次@PeterCordes建议更优雅的设置切换。

这就是看起来的样子。

function findUnique(arr) {
    var result = new Set();
    // iterate through array
    for(var i=0; i<arr.length; i++) {
        var v = arr[i];
        if(result.has(v)) { // even occurances
            result.delete(v);
        } else { // odd occurances
            result.add(v);
        }
    }
    // set to array O(n)
    var a = [], x = 0;
    result.forEach(function(v) { a[x++] = v; });
    return a;
}

JSFiddle http://jsfiddle.net/hepsyqyw/

答案 4 :(得分:-3)

  1. 创建一个包含counts个广告位的数组INT_MAX,每个元素都初始化为零。

  2. 对于输入列表中的每个元素,将counts[element]递增1。 (编辑:实际上,您需要执行counts[element] = (counts_element+1)%2,否则您可能会溢出值非常大的N值。执行此类模数计数是可以接受的,因为所有重复项都出现偶数次)

  3. 遍历counts,直到找到包含“1”的广告位。返回该插槽的索引。

  4. 步骤2是O(N)时间。步骤1和3占用了大量内存和大量时间,但两者都没有与输入列表的大小成比例,因此它们在技术上仍然是O(1)。

    (注意:这假设整数具有最小值和最大值,就像许多编程语言一样。)