原题:
有一个数组,其中有一个数字只出现了一次,其余的数字均出现了两次,请找到该数组.
class Solution {
public:
int FindNumber(vector<int> vec) {
int len = vec.size();
if (len == 0) return -1;
int res = 0;
for (int i = 0; i < len; i++) {
res = res^vec[i];
}
return res;
}
};
这种题目可以通过排序,或者建立哈希表查找。但排序复杂度为O(n log n),建立哈希表则是需要额外的O(n)空间复杂度。
通过位操作则可以巧妙解决该问题。
注意到,异或操作的特点: 1)两个相同的数异或结果为0,
2)与0异或得到该数本身,
3) 异或操作满足交换性。
基于这两个性质,上题可以这样解决:异或遍历整个数组,则所有出现两次的数字异或之后结果为0,0与只出现了一次的数字异或结果是该数本身。
所以最终的结果就是数组中所有数字异或的结果。
进阶版:
进阶一:
一个数组中,有两个数字只出现了一次,其余的数字都出现了两次,求找到出现一次的两个数字分别是哪个
分析:如果是一个数字只出现一次,直接有原型解决。现在出现了两个只出现一次的,该如何解决呢?
注意到,两个不同的数字异或结果不为0,即总有一位是1(也可能有多个位置为1)。正是这一位的结果构成了两个不同数之间的区别。
根据该位置为1和为0,我们可以将原数组分为两类,一个是该位置为1的,必然包括一个只出现一次的数字和多组出现两次的数字(两个相同的数字每一位的bit表示都一样,所以必然会被分到同一类中);另一类是该位置为0的。分别对每一类做异或操作,得到结果即为所求。
#include<iostream>
#include<vector>
using namespace std;
class Solution {
public:
void FindNumsAppearOnce(vector<int> data, int* num1, int *num2) {
int len = data.size();
if (len < 2) return;
int res = 0;
for (int i = 0; i < len; i++) {
res ^= data[i];
}
int idx = FindBit1(res);
*num1 = 0; *num2 = 0;
for (int i = 0; i< len; i++) {
if (isBit1(data[i], idx)) {
*num1 = *num1 ^ data[i];
}
else {
*num2 = *num2 ^ data[i];
}
}
}
bool isBit1(int num, int k) { //从右边数第k位是否为bit 1
int count = 1;
unsigned int flag = 1;
while (count < k) {
flag = (flag << 1);
count++;
}
if (flag & num) return true;
return false;
}
int FindBit1(int num) {
unsigned int flag = 1;
int count = 1;
while (!(num & flag)) {
flag = (flag << 1);
count++;
}
return count;
}
};
int main()
{
Solution s;
vector<int> vec{ 1,2,2,1,5,3,5,9 };
int num1 = 0; int num2 = 0;
s.FindNumsAppearOnce(vec, &num1, &num2);
cout << num1 << " " << num2 << endl;
return 0;
}
进阶二:
一个数组中,只有一个数字出现了一次,其余的数字均出现了三次,求找到只出现一次的数字。
分析:
因为三个相同的数字异或的结果还是其本身,所以上面题目的思路无法适用此题。但可以延续位操作的思路。
对每一个出现三次的数字来说,其二进制表示完全相同,如果把二进制表示的每一位都分别加起来,则每一位的和都可以被3整除。
所以,将所有的数字二进制表示的每一位都分别加起来之后,每个无法被3整除的位都是因为只出现了一次的那个数字造成的。
#include<iostream>
#include<vector>
using namespace std;
class Solution {
public:
int FindNumber(vector<int> vec) {
int len = vec.size();
if (len == 0) return -1;
return Find(BitSum(vec), BitSum(vec).size());
}
vector<int> BitSum(vector<int> vec) {
int len_res = sizeof(int) * 8;
vector<int> res(len_res);
for (int i = 0; i < vec.size(); i++) {
unsigned int flag = 1;
for (int j = len_res-1; j >= 0; j--) {
if (vec[i] & flag) {
res[j]++;
}
flag = flag << 1;
}
}
return res;
}
int Find(vector<int> res, int length) {
int num = 0;
int flag = 1;
for (int i = length - 1; i >= 0; i--) {
if (res[i] % 3) {
num += flag;
}
flag = flag << 1;
}
return num;
}
};
int main()
{
Solution s;
vector<int> vec{ 1,2,2,1,1,3,2};
int res = s.FindNumber(vec);
return 0;
}