首先,先来想一个简单的情况,就是数组中只有一个出现一次的数字,其余的数字都出现偶数次,找出这个只出现一次的数字。如果对异或熟悉的话,很容易想到使用异或来对数组中出现偶数次的数字进行过滤,因为对同一个数字异或偶数次,相当于没有改变。那么,我们对数组中的所有数字都进行异或的话,最后得到的结果就是我们要找的数字了。明白了这种简单的情况,我们再来分析下面的两题。
剑指 Offer 56 - I. 数组中数字出现的次数
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
示例 1:
输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]
示例 2:
输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] 或 [10,2]
限制:
2 <= nums.length <= 10000
思路:有了之前寻找数组中只出现一次的一个数据做铺垫,再解决这道题,我们应该想着能不能把相同的数字分到同一组,把这两个只出现一次的数字分到不同的组里。应该怎样来实现这样的操作呢?通过对数组中的所有数字进行异或操作,我们最后得到的结果等价于这两个只出现一次的数字进行异或的结果。如果把这个结果写成二进制的形式,那么,结果中的各个位代表着什么意思呢?根据异或的特点我们可以发现,结果中从后往前数,第一个出现‘1’的位置可以看做是这个问题的一个tips。通过这样的一个特殊的位我们可以将要求的数字分到不同的组里,也可以把相同的数字分到同一个组里(相同数字同一位置的二进制位肯定是一样的)。那么,这时我们再对同一个组里的数字进行异或操作,就可以得出要求的数字了。
Code:
class Solution { public: vector<int> singleNumbers(vector<int>& nums) { int res = 0, a = 0, b = 0; for (int n : nums) res ^= n; int h = 1; while ((h & res) == 0) h = h << 1; for (int n : nums) { if ((n & h) == 0) a = a ^ n; else b = b ^ n; } vector<int> ans; ans.push_back(a); ans.push_back(b); return ans; } };
剑指 Offer 56 - II. 数组中数字出现的次数 II
在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。
示例 1:
输入:nums = [3,4,3,3]
输出:4
示例 2:
输入:nums = [9,1,7,9,7,9,7]
输出:1
限制:
1 <= nums.length <= 10000
1 <= nums[i] < 2^31
思路:int型数据占32位,对每一位上出现的1的次数进行计数,统计完成之后,再对每一位上的计数值%3,这样最后剩下的就是只出现一次的那个数字了。方法想到之后,还要通过代码来实现,如果不经常使用位运算的话,实现起来会比较吃力。
Code:
class Solution { public: int singleNumber(vector<int>& nums) { vector<int> count(32, 0); for (int n : nums) { for (int i = 0; i < 31; ++i) { count[i] += (n >> i & 1) == 1 ? 1 : 0; } } int ans = 0; for (int i = 31; i >= 0; --i) { ans = ans << 1; if (count[i] % 3 == 1) { ans = ans | 1; } } return ans; } };