我正在尝试回答下面的问题:你有一个整数数组,这样每个整数都存在奇数个时间,除了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)空间做更好的方法吗?
感谢。
答案 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值)。
答案 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)。
可以在下面的图像中找到另一个增强,但仍然具有如前所述的最坏情况复杂性。
最后我相信,对于这个问题还有另一种更好的解决方案,但我决定分享我的想法。
图像中的算法可能不清楚,这里是算法的一些解释
它首先尝试根据位来划分元素,换句话说,将位作为滤波器,在每个阶段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); } }
根据评论中的讨论,以下几点值得注意: