作者:翟天保Steven
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处
题目描述:
一个整型数组里除了两个数字只出现一次,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
数据范围:数组长度2≤n≤1000,数组中每个数的大小0<val≤1000000
要求:空间复杂度O(1),时间复杂度O(n)
提示:输出时按非降序排列。
示例:
输入:
[1,4,1,6]
返回值:
[4,6]
说明:
返回的结果中较小的数排在前面
解题思路:
本题考察位运算。两种解题思路。
1)暴力法
使用哈希表记录出现频率,将频率为1的数输出即可。该方法的空间复杂度不符合题目要求。
2)异或运算
异或运算指两个数相同为0,不相同为1。因为除了要寻找的数字出现了一次,其他数字都出现了两次,所以将这些数字进行异或运算后,根据其特性,重复出现的数字抵消了,只保留了出现了一次的数字。
如4^1^2^1^2。4为100,1为001,2为010,4^1得到101,再^2得到111,再^1得到110,再^2得到100,即4。
那如果有两个出现了一次的数字,则结果就相当于这两个数字异或。如4^1^2^1^2^3,最终的结果就是111,即4^3。
本题目要求输出这两个数字,那如何将111拆开呢?4和3,如果某一位不相同,则异或结果该位就是1,我们定义t从001开始,将t与111与运算,寻找哪一位首次出现了1;111中最右即为1,说明在00x的位置,数字4和3是不一样的;再次遍历,将所有数据根据该位的数字分类,这样就能得到两个组,数字为1的组结果就是3,数字为0的组结果就是4。
不得不说,这题目就是为这个解法量身定做的。。。各个条件完美匹配emm
测试代码:
1)暴力法
class Solution {
public:
// 寻找出现一次的数字
vector<int> FindNumsAppearOnce(vector<int>& nums) {
unordered_map<int,int> um;
vector<int> result;
// 遍历数组
int size = int(nums.size());
for(int i = 0; i < size; ++i){
um[nums[i]]++;
}
// 寻找出现频率为1的数
for(int i = 0; i < size; ++i){{
if(um[nums[i]] == 1){
result.emplace_back(nums[i]);
}
}}
// 按大小顺序输出
if(result[0] < result[1]){
return result;
}
else{
return { result[1], result[0] };
}
}
};
2)异或运算
class Solution {
public:
// 寻找出现一次的数字
vector<int> FindNumsAppearOnce(vector<int>& nums) {
vector<int> result{ 0, 0};
// 遍历数组进行异或运算
int temp = 0;
int size = int(nums.size());
for(int i = 0; i < size; ++i){
temp ^= nums[i];
}
// 找到两个数不相同的第一位
int t = 1;
while((t & temp) == 0){
t <<= 1;
}
// 再次遍历,将t位为1的数归为1组,为0的数归为1组,这样两组的异或运算得到的结果就是两个不重复数
for(int i = 0; i < size; ++i){
if((t & nums[i]) == 0){
result[0] ^= nums[i];
}
else{
result[1] ^= nums[i];
}
}
// 按大小顺序输出
if(result[0] < result[1]){
return result;
}
else{
return { result[1], result[0] };
}
}
};