剑指 Offer(第2版)面试题 56:数组中数字出现的次数
剑指 Offer(第2版)面试题 56:数组中数字出现的次数
题目一:数组中只出现一次的两个数字
题目描述:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。你可以假设这两个数字一定存在。
要求时间复杂度是 O(n),空间复杂度是 O(1)。
算法:
- 设 num1 和 num2 是数组中只出现一次的两个数字,对数组 nums 求异或和 numsXor,numsXor = num1 ^ num2。
- 因为 num1 和 num2 不一样,所以它们异或的结果不为 0。求 numsXor 的最低位的第一个 1,记为 indexOf1。我们以第 indexOf1 位是不是 1 为标准把原数组的数字分成两个子数组,第一个子数组中每个数字的第 indexOf1 位都是 1,而第二个子数组中每个数字的第 indexOf1 位都是 0。
- 按这样的标准分配,出现了两次的数字一定分配到同一个数组里,而 num1 和 num2 一定分配到不同的数组里,问题转换为在数组中找到唯一一个出现一次的数字。
代码:
class Solution
{
public:
vector<int> findNumsAppearOnce(vector<int> &nums)
{
if (nums.size() < 2)
return {};
int numsXor = 0;
for (int &num : nums)
numsXor ^= num;
unsigned int indexOf1 = FindFirstBit1(numsXor);
int num1 = 0, num2 = 0;
for (int &num : nums)
{
if (isBit1(num, indexOf1))
num1 ^= num;
else
num2 ^= num;
}
return {num1, num2};
}
// 辅函数 - 求出数 x 的最低位的 1 的位数
unsigned int FindFirstBit1(int x)
{
int index = 0;
while (((x & 1) == 0) && index < 8 * sizeof(int))
{
x >>= 1;
index++;
}
return index;
}
// 辅函数 - 判断数 x 的第 indexBit 位是不是 1
bool isBit1(int x, unsigned int indexBit)
{
x >>= indexBit;
return x & 01;
}
};
复杂度分析:
时间复杂度:O(n),其中 n 是数组 nums 的元素个数。
空间复杂度:O(1)。
题目二:数组中唯一只出现一次的数字
题目描述:在一个数组中除了一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。你可以假设满足条件的数字一定存在。
要求只使用 O(n) 的时间和额外 O(1) 的空间。
我们把数组中的所有数字的二进制表示的每一位都加起来。如果某一位的和能被 3 整除,那么那个只出现一次的数字二进制中对应的那一位是 0,否则是 1。
代码:
class Solution
{
private:
const int BITS = 32;
public:
int findNumberAppearingOnce(vector<int> &nums)
{
int ans = 0;
for (int i = 0; i < BITS; i++)
{
int cnt1 = 0;
for (int &num : nums)
cnt1 += (num >> i) & 01;
if (cnt1 % 3 == 1)
ans |= 1 << i;
}
return ans;
}
};
复杂度分析:
时间复杂度:O(BITS * n),其中 BITS = 32,是 int 类型的位数,n 是数组 nums 的元素个数。
空间复杂度:O(1)。