(1)整型数组里除了一个数字之外,其它的数字都出现了两次,找出这个只出现一次的数字;
(2)整型数组除了2个数字只出现一次外,其他数字成对出现,找出落单的2个数;
(3)数组中每个数会出现3次,只有1个例外的数仅出现1次,找出该数字;
-------------
(1)整型数组里除了一个数字之外,其它的数字都出现了两次,找出这个只出现一次的数字
要求时间复杂度是O(n),空间复杂度是O(1)。
(1.1)分析
由于题目要求时间复杂度为O(n),所以先排序然后比较相邻数字是否相同的思路被排除。
空间复杂度是O(1),辅助空间被限制,所以hash表的思路也被排除。
那么这个题的突破口在哪里呢?注意这个数组的特殊性:其它数字都出现了两次,只有一个数出现了一次。可以想到运用异或运算,任何一个数字异或它自己都等于0。
如果我们从头到尾依次异或数组中的每一个数,那么最终的结果就是那个只出现一次的数字,因为其他出现两次的数字全部在异或中被抵消为0了(异或运算遵循交换分配率)。
举个栗子:2 3 4 2 3
所有数字依次异或运算:2 xor 3 xor 4 xor 2 xor 3 = (2 xor 2) xor (3 xor 3) xor 4= 0 xor 0 xor 4 = 4
最终结果4就是我们要找的那个只出现一次的数字。
(1.2)实现
void FindNumsAppearOnce(int data[], int length, int &num1)
{
if (length < 2)
return;
int resultExclusiveOR = 0;
for (int i = 0; i < length; ++ i) {
resultExclusiveOR ^= data[i];
}
num1=resultExclusiveOR ;
}
(1.3)异或的特点
异或操作^是很特殊的。
1)自己和自己异或,结果会得到全0;
2)和全0异或,结果不变;
3)和全1异或,结果会得到自己的取反。
-----------
(2)整型数组除了2个数字只出现一次外,其他数字成对出现,找出落单的2个数;
(2.1)分析
需要将序列分成2类,正好把两个落单的数划分到两个类中去。
1)第一次先把序列异或起来,得到的结果其实就是两个落单的数的异或。
分析结果,结果的二进制中为1的位为这两个数不同的,从中随便选一个位作为“分类标准”(我选的是最右边的1)。
找到第一个为1的位的位置,记为第N位。现在我们以第N位是不是1为标准把原数组中的数字分成两个子数组,第一个子数组中每个数字的第N位都为1,而第二个子数组的每个数字的第N位都为0。
2) 分类之后,就变为上一问题了(序列中只有一个落单的数)。根据分类标准,将各类中的数字异或起来。就得到了两个落单的数。
(2.2)实现
void FindNumsAppearOnce(int data[], int length, int &num1, int &num2)
{
if (length < 2)
return;
// get num1 ^ num2
int resultExclusiveOR = 0;
for (int i = 0; i < length; ++ i)
resultExclusiveOR ^= data[i];
// get index of the first bit, which is 1 in resultExclusiveOR
unsigned int indexOf1 = FindFirstBitIs1(resultExclusiveOR);
num1 = num2 = 0;
for (int j = 0; j < length; ++ j)
{
// divide the numbers in data into two groups,
// the indexOf1 bit of numbers in the first group is 1,
// while in the second group is 0
if(IsBit1(data[j], indexOf1))
num1 ^= data[j];
else
num2 ^= data[j];
}
}
// 从最低位开始找,直到找到num中第一个非0的位置,得到该位数;
unsigned int FindFirstBitIs1(int num)
{
int indexBit = 0;
while (((num & 1) == 0) && (indexBit < 32))
{
num = num >> 1;
++ indexBit;
}
return indexBit;
}
// 判断某个数,第 indexBit位是否为1;
bool IsBit1(int num, unsigned int indexBit)
{
num = num >> indexBit;
return (num & 1);
}
--------
(3)数组中每个数会出现3次,只有1个例外的数仅出现1次,找出该数字;
要用O(N)时间, O(1)空间。
(3.1)方法一
(3.1.1)分析
int类型有32位,对数组中除了single数以外的所有数的每一位做累加,
32位每一位上的累加值都是3或者0;
对每一位的累加值做模3加法的话,32位每一位的值应该有1有0;
三个相同数加起来都会模3变为0,所以剩下的1都是属于single数的,所以这个累加和就等于single数。
时间是O(32*N),但要分别计算每一位的累加,分别作32次累加。空间是O(1)。
(3.1.2)实现
int singleNumber(int A[], int n) {
int result = 0;
int sum;
int i, j;
for(i=0;i<32;i++)
{
sum = 0;
for(j=0;j<n;j++)
sum += ((A[j]>>i)&1); //每轮只对固定某一位做累加
if(sum%3 == 1)
result = result|(1<<i);
}
return result;
}