面试题56 - I. 数组中数字出现的次数
题目:
解答:
方法一:哈希
1 class Solution { 2 public: 3 vector<int> singleNumber(vector<int>& nums) { 4 map<int, int> count; 5 for (int n : nums) count[n] ++; 6 vector<int> res; 7 for (auto p : count) 8 if (p.second == 1) 9 res.push_back(p.first); 10 return res; 11 } 12 };
方法二:异或
异或
想到这个系列的第一个题,就是找出单个的只出现一次的字符。做法是异或操作。这个题也是用异或。
把所有的数字进行一次异或,得到的是只出现了一次的两个数字的异或。
这两个数字不等,因此他们的二进制必定至少1位不同,即异或结果中为1的那位(一个数字的该位为1,另个数字的该位为0)。找出从右向左的第一个不同的位置(异或值为1的位置),给mask在该位置设置成1,mask的其余位置是0. mask存在的意义在于我们能通过该位置来分辨出两个只出现了一次的数字。
然后技巧性的来了:再进行一次异或操作。
每个数字都跟mask相与。通过与的结果为0和为1,即可区分出两个数字。
我刚开始有点不明白的是,为什么把所有的元素都重新异或了?其实,因为除了这两个元素以外,其他的元素都出现了两次,这两次相同的数字的和mask的与操作的结果是相同的,所以会被异或两次抵消掉。
一言以蔽之,先通过异或找出两个元素的异或结果。再根据异或结果的出现为1的位置作为mask,mask的二进制只有1位是1,也就是只看两个元素的该位置。最后,通过与操作判断该位置是0还是1区分两个元素。
1 class Solution { 2 public: 3 vector<int> singleNumbers(vector<int>& nums) 4 { 5 int res = 0; 6 for (int n : nums) 7 { 8 res ^= n; 9 } 10 11 int a = 0, b = 0; 12 int mask = 1; 13 while ((mask & res) == 0) 14 { 15 mask <<= 1; 16 } 17 for (int n : nums) 18 { 19 if (n & mask) 20 a ^= n; 21 else 22 b ^= n; 23 } 24 return {a, b}; 25 } 26 };