题目描述:
一个整型数组里除了两个数字只出现一次,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
数据范围:数组长度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] }; } } };