我也遇到一个面试题 冥思苦想不出 夜不能寐 求解

2013-12-27 11:32:35 +08:00
 pagict
说在一个整型数组里,所有的数都重复了两次,仅有两个数是不重复的,如何在时间 O(n) 和空间 O(1) 内找出这两个数?

想了好久,关键是空间复杂度不符合。有种被虐的痛苦
4783 次点击
所在节点    问与答
37 条回复
cxe2v
2013-12-27 11:39:07 +08:00
数组是有序还是无序的?
34D
2013-12-27 11:40:15 +08:00
异或。
pagict
2013-12-27 11:52:51 +08:00
@cxe2v 没提,想来是无序的。要是有序那就好做了

@34D 分别跟谁异或啊
dingyaguang117
2013-12-27 12:04:59 +08:00
2个数不重复啊。。
vainly
2013-12-27 12:41:10 +08:00
//用来存放找出来的数据
Map<Integer,Integer> map = new HashMap<Integer,Integer>();
//整数数组
Integer[] array = {1,1,2,2,3,3,4,5,5,6,6,7};
for(int i=0;i<array.length;i++){
Integer key = array[i];
if(map.get(key)==null){
map.put(key, 1);
}else{
map.remove(key);
}
}
//输出结果
System.out.println(map.toString());
327beckham
2013-12-27 12:46:24 +08:00
如果不是编程之美上有这个题目,就是剑指offer上有这个题目。
先从只有一个数不重复的题目扩展开来即可。
327beckham
2013-12-27 12:52:18 +08:00
还是直接说答案吧,如果是只有一个数不重复的话,所有数字抑或,即可找到那个数。
现在扩展成两个数了,那么第一步还是全部异或,最后剩下的那个数A,就是两个单独数字的异或了。这个时候,在A中随便找一个某个bit为1的位置。按照这个bit是否为1,可以将题目中的原有的数组分成两个数组了,而且这两个数组中分别有一个只出现一次的数。
pagict
2013-12-27 12:53:54 +08:00
@dingyaguang117 形如 [1,2,3,4,5,4,7,1,2,3]

@vainly 空间复杂度不对啊

@327beckham 是有两个数不重复
327beckham
2013-12-27 12:54:46 +08:00
时间复杂度:整个数组扫描两遍,空间复杂度:记录几个变量即可。
327beckham
2013-12-27 12:59:22 +08:00
@pagict 跟你说个最最简单的例子吧:1 64 3 3 4 4 5 5 6 6 7 7这个数组全部异或之后,肯定得到的是1和64的异或的结果,得到的数字是3.二进制也就是0100 0001. 从右往左数的第1个bit是个1吧。那就按照第一个bit是否为1,可以将原有数组的所有数字分成两组了。
327beckham
2013-12-27 13:00:06 +08:00
@pagict 上面一句写错了,1异或64是65.。。我写成3了
pagict
2013-12-27 13:03:31 +08:00
@327beckham 异或是每个数组元素和谁异或
anewg
2013-12-27 13:07:20 +08:00
@327beckham 没看懂你的解法,楼主的题目是要时间 O(n) 和空间 O(1),你这个再分数组不就超过了吗?而且分成两组然后呢?
anewg
2013-12-27 13:08:35 +08:00
@pagict 两两异或过去,相同的元素异或都会为0,最后0跟那个没重复的单个元素异或得那元素本身
pagict
2013-12-27 13:11:35 +08:00
@anewg 题目没说已经归类好了啊 若是形如 [1,2,3,4,5,4,7,1,2,3] 呢
anewg
2013-12-27 13:18:15 +08:00
@pagict 那个异或的只针对1个不重复
327beckham
2013-12-27 13:39:32 +08:00
@anewg 我说的分组也就是 逻辑理解上分组而已嘛,并没有说另外开个空间存这两个数组啊。第二次扫描原有数组的时候,某一类异或结果存到X,另外一类异或存到Y。最后答案就是X和Y这两个数了嘛。
327beckham
2013-12-27 13:41:36 +08:00
@pagict 1异或2异或3和 2异或3异或1,结果一样,交换律。不在乎原有顺序
biaobiaoqi
2013-12-27 14:45:12 +08:00
@327beckham 很赞的思路!
Sdhjt
2013-12-27 14:50:33 +08:00
@327beckham 思路很赞,只需要扫描两遍数组就可以。下面用Go实现了一个例子:

package main

import (
"fmt"
)

func main() {
nums := []int{23, 23, 19, 19, 1, 88, 88, 3, 3, 2, 56, 56}

//设两个不重复的数为a1和a2,x = a1 ^ a2,bits为a1和a2某个不一致的位
var a1, a2, x, bits int

//将所有的数字异或,得到的结果就为x,因为重复的数经过异或后都为0
for _, v := range nums {
x = x ^ v
}

//找出a1和a2某个不一致的位,换句话说,就是找出x为1的位(当然,x为1的位有很多,我们这找的是x从右往左第一个为1的位)
bits = 1
for i := 31; i >= 0; i++ {
if x&bits != 0 {
break
}
bits = bits << 1
}

//舍去所有bits位为0的位,将剩下的数字全部异或,这样就能得出两个不重复的数字其中的一个
for _, v := range nums {
if v&bits != 0 {
a1 = a1 ^ v
}
}

//根据x和a1可以很容易求出a2
a2 = x ^ a1

fmt.Println("Result : ", a1, a2)
}

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/94742

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX