描述
一个整型数组里除了两个数字只出现一次,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
数据范围:数组长度 ,数组中每个数的大小
要求:空间复杂度 ,时间复杂度
提示:输出时按非降序排列。
示例1
输入:
[1,4,1,6]
复制返回值:
[4,6]
复制说明:
返回的结果中较小的数排在前面
示例2
输入:
[1,2,3,3,2,9]
复制返回值:
[1,9]
题解
hash表解法
代码省略~
时间复杂度O(N),空间复杂度O(N)
位运算——异或
该解法为牛客网提供
个人注解:
如果一个数组中只有一个数字出现一次,其他的都出现2次,那么通过N次异或,最终可以得到只出现一次的数字。但是题目中有2个只出现一次的数字,如果通过一次完整的遍历异或得到的结果就是这两个数字的异或结果。
假设a⊕b=c,且c不等于0,那么c中一定有某一位的二进制值为1,a和b在该位的二进制值一定是一个为0,一个为1。此时我们可以将整个数组中的 元素分成2类,一类为在该位为1的,另一类为该位为0的。在遍历的时候将这两类数字的异或结果分别放在2个数字中,这两个数字的最终结果就是题解。
思路:
异或运算满足交换率,且相同的数字作异或会被抵消掉,比如:,且任何数字与0异或还是原数字,放到这个题目里面所有数字异或运算就会得到,也即得到了两个只出现一次的数字的异或和。
//遍历数组得到a^b
for(int i = 0; i < array.length; i++)
temp ^= array[i];
但是我们是要将其分开得到结果的,可以考虑将数组分成两部分,一部分为,另一部分为的样式,怎么划分才能让a与b完全分开,而另外的也能刚好成对在一个组呢?这是我们需要考虑的问题。
的结果中如果二进制第一位是1,则说明a与b的第一位二进制不相同,否则则是相同的,从结果二进制的最高位开始遍历,总能找到二进制位为1的情况:
//找到两个数不相同的第一位
while((k & temp) == 0)
k <<= 1;
因为两个数字不相同,我们就以这一位是否为1来划分上述的两个数组,相同的数字自然会被划分到另一边,而a与b也会刚好被分开。
//遍历数组,对每个数分类
if((k & array[i]) == 0)
res1 ^= array[i];
else
res2 ^= array[i];
具体做法:
- step 1:遍历整个数组,将每个元素逐个异或运算,得到。
- step 2:我们可以考虑位运算的性质,找到二进制中第一个不相同的位,将所有数组划分成两组。
- step 3:遍历数组对分开的数组单独作异或连算。
- step 4:最后整理次序输出。
图示:
代码如下:
class Solution {
public:
vector<int> FindNumsAppearOnce(vector<int>& array) {
vector<int> res(2, 0);
int temp = 0;
//遍历数组得到a^b
for(int i = 0; i < array.size(); i++)
temp ^= array[i];
int k = 1;
//找到两个数不相同的第一位
while((k & temp) == 0)
k <<= 1;
for(int i = 0; i < array.size(); i++){
//遍历数组,对每个数分类
if((k & array[i]) == 0)
res[0] ^= array[i];
else
res[1] ^= array[i];
}
//整理次序
if(res[0] < res[1])
return res;
else
return {res[1], res[0]};
}
};