LeetCode -- 数字出现的次数类
problem1
1. 题目描述
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
2. 思路
找一个不同点,将所有数字分为两类。
其中,两个只出现一次的数字各出现在一个集合当中。
如何找最后一个不同点?
我们知道,如果两个数字不同的话,那么,在二进制表示下,他们一定有一个二进制位是不同的,其中一个为 \(0\),一个为 \(1\)。
通过对所有数字求异或和,就可以得到 \(a\) ^ \(b\),然后枚举一下,任意找一个不同点即可!
3. 代码
class Solution {
public:
vector<int> singleNumbers(vector<int>& nums) {
int xors = 0;
for(auto &x : nums) xors ^= x;
int diff = 0; // 二进制表示中第一个不同点
for(int i = 0; i < 32; i ++ ) {
if(xors >> i & 1) {
diff = i;
break;
}
}
int r1 = 0, r2 = 0;
for(auto &x : nums) {
if(x >> diff & 1) r1 ^= x;
else r2 ^= x;
}
return {r1, r2};
}
};
problem2
1. 题目描述
2. 思路
(1) 卡诺图
卡诺图做法,然而我不太懂卡诺图。。
class Solution {
public int singleNumber(int[] nums) {
// 可以设计一种逻辑,使数字出现 3 次时,该逻辑的结果为 0(即只有 0,1,2 三种状态)
// 其实就是一个 三进制
// 一位二进制数只能存储 0 和 1 两种状态,所以我们需要用到两位二进制
// 设两位二进制数的高位为 A,低位为 B。C 是输入变量
// 表示的三种情况为 : 0次:00(A=0,B=0), 1次:01(A=0,B=1), 2次:10(A=1,B=0)
// 注:11(A=1,B=1) 为无效输入
// 画出关于 A 的卡诺图(AB为11的结果是不重要的,用 x 表示):
// AB\C | 0 | 1
// =================
// 00 | 0 | 0
// 01 | 0 | 1 ====> 得到 A = BC + AC'
// 11 | x | x
// 10 | 1 | 0
// 画出关于 B 的卡诺图
// AB\C | 0 | 1
// =================
// 00 | 0 | 1
// 01 | 1 | 0 ====> 得到 B = BC' + A'B'C
// 11 | x | x
// 10 | 0 | 0
// 很明显啊,我们需要的就是只出现一次的情况 01(A=0,B=1),即 B 的结果
int A = 0, B = 0;
for (int C : nums) {
int tmp = A;
A = (B & C) | (A & ~C);
B = (B & ~C) | (~tmp & ~B & C);
}
return B;
}
}