如何使空间复杂度为O(1)

时间:2016-01-15 19:56:28

标签: algorithm data-structures bit-manipulation time-complexity space-complexity

我正在尝试回答下面的问题:你有一个整数数组,这样每个整数都存在奇数个时间,除了3个。找到三个数字。

到目前为止,我采用了暴力方法:

 public static void main(String[] args) {
    // TODO Auto-generated method stub

    int number[] = { 1, 6, 4, 1, 4, 5, 8, 8, 4, 6, 8, 8, 9, 7, 9, 5, 9 };
    FindEvenOccurance findEven = new FindEvenOccurance();
    findEven.getEvenDuplicates(number);

  }

  // Brute force
  private void getEvenDuplicates(int[] number) {

    Map<Integer, Integer> map = new HashMap<Integer, Integer>();

    for (int i : number) {

      if (map.containsKey(i)) {
        // a XOR a XOR a ---- - -- - - odd times = a
        // a XOR a ---- -- -- --- - even times = 0
        int value = map.get(i) ^ i;
        map.put(i,value);
      } else {
        map.put(i, i);
      }
    }

    for (Entry<Integer, Integer> entry : map.entrySet()) {

      if (entry.getValue() == 0) {
        System.out.println(entry.getKey());
      }

    }
  }

它工作正常,但效率不高。

o / p:

1
5
6
8

但问题指出我们需要在O(1)空间和O(N)时间复杂度中做到这一点。对于我的解决方案,时间复杂度是O(N)但空间也是O(N)。有人可以建议我用O(1)空间做更好的方法吗?

感谢。

6 个答案:

答案 0 :(得分:4)

我花了一些时间来解决这个问题。似乎我找到了解决方案。无论如何,我相信,该社区将帮助我检查下面列出的想法。

首先,我声称当非配对整数的数量等于 1 2 时,我们可以解决这个问题。在 1 非配对整数的情况下,我们只需找到所有数组元素的 XOR ,它就是答案。在 2 的情况下,非配对整数解决方案变得更加复杂。但是之前已经讨论过了。例如,您可以找到它here

现在让我们尝试在非配对整数的数量等于 3 时解决问题。

一开始我们还计算所有元素的 XOR 。我们将其表示为 X

考虑 X 中的 i-th 位。我假设它等于 0 。如果它等于 1 ,则下一个程序几乎相同,我们只需将 0 更改为 1 ,反之亦然。

因此,如果 X 位中的 i-th 等于 0 ,我们有两种可能的情况。一种情况是所有非配对整数在 i-th 位中具有 0 。另一种情况是,一个非配对整数在 i-th 位中具有 0 ,而两个非配对整数在中具有 1 >第i个位。此语句基于简单的 XOR 操作属性。所以我们在 i-th 位中有一个或三个非配对的整数 0

现在让我们将所有元素分成两组。第一组是 0 0 的整数,第二组是 i中 1 的整数-th 位位置。此外,我们的第一个组包含一个或三个非配对整数,在 i-th 位中为'0'。

我们如何在第一组中获得一定数量的非配对整数?我们只需要计算第二组中所有元素的 XOR 。如果它等于零,则所有非配对整数都在第一组中,我们需要检查另一个 i 。在其他情况下,第一组中只有一个非配对整数,第二组中有另外两个,我们可以使用本答案开头的方法分别为这两个组解决问题。

关键观察是 i 使得一个非配对整数的 i-th 位与 i-th 不同两个其他非配对整数的位。在这种情况下,非配对整数都在两组中。它基于以下事实:如果没有这样的 i ,那么非配对整数中所有位置的位都是相似的,并且它们彼此相等。但根据问题陈述,这是不可能的。

此解决方案无需任何额外内存即可实现。总复杂度是线性的,有些常数取决于数组元素中的位数。

答案 1 :(得分:3)

不幸的是,如果我们使用严格的空间感,就不可能实现具有O(1)空间和O(n)复杂度的这种解决方案,即O(1)空间受输入中使用的最大空间的约束阵列。

在一个微弱的空间意义上,一个任意大的整数数仍然适合O(1),你可以将你的计数器编码成这个整数的位。从所有位设置为1开始。当您在输入数组中遇到数字n时,切换第n位。最后剩余1的所有位代表偶数次遇到的3个数字。

答案 2 :(得分:1)

有两种方法可以查看您的问题。

第一种方式,作为具有无限整数整数的数学问题,似乎无法解决。

第二种方式,作为有限整数集的计算问题,你已经解决了它(恭喜!)。为什么?因为存储空间受MAX_INT限制,与N无关。

注意,一个显而易见的空间优化只是存储值一次,删除偶数计数的前一个值,你将获得一半的空间。

关于@Lashane和@ SGM1的其他答案:他们也解决了“计算”问题,但在大多数现实世界的场景中,可以说比其他人更少更高效。为什么?因为它们预先分配512MB阵列,而不是按比例分配数组中不同值的数量。由于数组可能使用远小于MAX_INT的不同值,因此即使为每个值存储32位而不是1,也可能使用小于512MB的数据。这是32位整数,前面有更多位分配的数组将呈指数级增长,OTOH您的解决方案仅取决于数组中的实际值,因此不受系统位数的影响(即max int值)。

另请参阅thisthis以获得更好(更少空间)的算法。

答案 3 :(得分:1)

例如,考虑允许的数字大小 4位,这意味着允许的数字范围从0到2 4 -1 这是一个常数 16 ,对于我们在所有数组上运行的每个可能的输入和 xor 这个数字的出现,如果xor的结果为零,我们添加当前值总体结果。这个解决方案是 O(16N) O(N)并且只使用一个额外的变量来评估当前数字的xor,其中 O(1)< / strong>在空间复杂性方面。

我们可以将这个方法扩展到我们原来的问题,但是它在运行时复杂度方面会有一个非常大的常数,它将与原始输入中允许的位数成比例。

我们可以通过遍历所有元素来增强这种方法,并找到所有输入数据的最高位,假设它是10 th 位,那么我们的运行时复杂度将变为 O(2 < sup> 10 N),其也是 O(N)

可以在下面的图像中找到另一个增强,但仍然具有如前所述的最坏情况复杂性。 enter image description here

最后我相信,对于这个问题还有另一种更好的解决方案,但我决定分享我的想法。

编辑:

图像中的算法可能不清楚,这里是算法的一些解释 它首先尝试根据位来划分元素,换句话说,将位作为滤波器,在每个阶段xor分割元素,直到xor结果为零,然后值得检查这个组一个,因为它肯定会包含至少一个所需的输出。或者如果两个咨询过滤器的尺寸相同,我们将停止此过滤器,下面的示例将更加清楚 输入:1,6,4,1,4,5,8,8,4,6,8,8,9,7,9,5,9
我们首先根据最低有效位划分元素 1 st 位零:6,4,4,8,8,4,6,8,8
6 xor 4 xor 4 x或8 xor 8 xor 4 xor 6 xor 8 xor 8 = 4 所以我们将根据2 nd 位继续划分该组 1 st 位零 2 nd 位零:4,4,4,8,8,8, 8
4 xor 4 xor 4 x或8 xor 8 xor 8 xor 8 xor 8 = 4 所以我们将根据3 rd 位继续划分这个组 1 st 位零 2 nd 位零 3 rd 位零:8,8,8,8
8 xor 8 xor 8 xor 8 = 0
因此,我们将遍历此过滤器下的每个元素,因为xor的结果为零,到目前为止我们将向结果添加8。 1 st 位零 2 nd 位零 3 rd 第一位:4,4,4
4 xor 4 xor 4 = 4
1 st 位零 2 nd 位零 3 rd bit 1 4 th 位零:4,4,5
4 xor 4 xor 4 = 4 所以我们将在此停止,因为此过滤器包含与先前过滤器相同的大小 现在我们将回到1 st 和2 nd 位的过滤器 1 st 位零 2 nd 位一:6,6
6 xor 6 = 0 所以我们将遍历此过滤器下的每个元素,因为xor的结果为零,到目前为止我们将向结果添加6。 现在我们将回到1 st 位的过滤器 1 st bit one :9,5,9,7,9,1,1
现在我们将在此过滤器下继续执行相同的程序。
有关完整示例,请参见上图。

答案 4 :(得分:0)

您的问题大纲和示例不匹配。你说你在你的问题中寻找3个整数,但是这个例子显示了4个。

我不确定如果没有其他限制,这是可能的。在我看来,最坏的外壳尺寸复杂度总是至少为O(N-6)=&gt; O(N)没有排序列表和整数整数。

如果我们从排序数组开始,那么是,简单,但是没有指定此约束。我们自己对阵列进行排序将太时间或太复杂。

答案 5 :(得分:-2)

我以一种略有不同的方式使用Lashane的提议来回答答案:

char negBits[268435456]; // 2 ^ 28 = 2 ^ 30 (number of negative integer numbers) / 8 (size of char)
char posBits[268435456]; // ditto except positive

int number[] = { 1, 6, 4, 1, 4, 5, 8, 8, 4, 6, 8, 8, 9, 7, 9, 5, 9 };

for (int num : number){
   if (num < 0){
       num = -(num + 1);// Integer.MIN_VALUE would be excluded without this + 1
       negBits[ << 4] ^= ((num & 0xf) >> 1);
   }
   else {
       posBits[num << 4] ^= ((num & 0xf) >> 1);
       // grab the rite char to mess with
       // toggle the bit to represent the integer value.
   }
}

// Now the hard part, find what values after all the toggling:

for (int i = 0; i < Integer.MAX_VALUE; i++){
    if (negBits[i << 4] & ((i & 0xf) >> 1)){
        System.out.print(" " + (-i - 1));
    }
    if (posBits[i << 4] & ((i & 0xf) >> 1)){
        System.out.print(" " + i);
    }
}

根据评论中的讨论,以下几点值得注意:

  • 假设使用32位Java。
  • Java数组的固有限制为Integer.MAX_INT