剑指 Offer 56 - I. 数组中数字出现的次数
题目分析
- 学习了异或的性质。1.恒等律:X ⊕ 0 = X 2.归零律:X ⊕ X = 0 3.交换律A ⊕ B =B ⊕ A
4.结合律分配律也都满足。 - 回到题目上,简化问题,如果只有一个只出现过一次的数字,那么我们完全可以通过异或的性质来解决题目,初始化x=0,x去异或数组里面的每个数,出现过两次的数字会变成0,而且这跟数字的顺序没有关系,因为交换律在此做了支撑。所以最后x会直接 = 那个只出现过一次的数字。可惜题目中说有两个只出现过一次的数,那么情况就需要再分析分析。
- 首先我们第一次异或整个数组,因为出现过两次的数字全部会变成0,剩下的就是我们值出现过一次的两个数字异或的结果 Z = X ⊕ Y,这个结果是什么呢?XY是两个不一样的数字,所以至少有一位上,他们是不相同的,也就是分别为0,1。所以根据异或的性质,在这一位上,异或的结果Z是为1的。我们可以根据这一位的不同将原来一个数组分为两个数组,这样两个数组分别异或返回结果就解决问题了。重点是怎么根据这一位的不同将一个数组分为两个数组。
将Z与m做与运算,只有与到Z中第一位的(从右到左)1时与运算结果才不为0,否则其他情况下m左移一位。这样我们就得到了一个m.它除了那个位上为1外其他都为0,然后根据与这个M做与运算的结果可以将数组分为两个。
class Solution {
public:
vector<int> singleNumbers(vector<int>& nums) {
int x = 0, y = 0, n = 0, m = 1;
for(int num : nums) // 1. 遍历异或
n ^= num;
while((n & m) == 0) // 2. 循环左移,计算 m
m <<= 1;
for(int num : nums) { // 3. 遍历 nums 分组
if(num & m) x ^= num; // 4. 当 num & m != 0
else y ^= num; // 4. 当 num & m == 0
}
return vector<int> {x, y}; // 5. 返回出现一次的数字
}
};
剑指 Offer 56 - II. 数组中数字出现的次数 II
Difficult,状态级太难了,直接hash。
Leetcode: https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/9hyq1r/.