一:找出数组中出现一次的数。
思路:
利用双目运算中的“按位异或”,求出数组中出现一次的数。
为什么“异或”可以求出数组中出现一次的数?
相信第一次听到这个方法的小伙伴和我一样惊讶?卧槽,还有这操作?
让我们先了解下异或的性质:两个一位数相异或,两数不同返回1,相同返回0,也就是 0^1 = 1;1 ^ 1 = 0; 0 ^ 0 = 0;
所有有下面的交换律:
A ^ B = C;
C ^ A = B;
C ^ B = A;
画图举个例子:
也就是说,5 和 5 异或一下,得出的结果为0;
由此就可以应用到数组中,将数组中的元素与0相异或,其中相同的数异或得0,
最后剩下的那个数就是只出现一次的数!
附上代码:
#include<stdio.h>
int FindNum(const int* arr,int sz){
//先定义一个变量为0
int fun_x = 0;
int i = 0;
for (; i < sz; i++){
//将数组中的每个元素与与fun_x异或后保存到fun_x中
fun_x ^= arr[i];
}
//当数组中所有元素都异或一遍后,得出的就是只出现一次的元素
return fun_x;
}
//问题:找到数组中只出现一次的数。
int main(){
int arr[] = { 4, 2, 4, 8, 7, 2, 8, 9, 7 };
//数字的元素个数
int sz = sizeof(arr) / sizeof(arr[0]);
int x = 0;
x = FindNum(arr,sz);
printf("只出现一次的数是:%d\n",x);
return 0;
}
二:找出数组中两个出现一次的数。
按照(一)中的思路,如果照办过来使用的话,最后得出的结果为两个出现一次的数的异或结果。
那这个要怎么操作?
如果我们有一种方法,可以将他们两个数分开,然后再按照(一)中的方法不就很容易得到了吗?
so:
这时候又要用到异或了。
当两个数异或后,得到的是二进制位上不同的那位,例如:
由上图我们可以看到,10和7的异或结果二进制表示形式是1110,
我们来看右边数第二位,x的第二位为0,y的第二位为1。所以我们可以取这个右边数第二位,来作为分组依据
由此,我们就可以按照位数的差异性,来找这两个数。
我们上面讨论了两个相同的数字进行异或的结果为0。所以只要一个数满足二进制表示形式下的右边数第二位数与我们取出分组依据的数相同,就可以把他放到一个组内。不满足的放到另外一个组内。
最后我们将这两个组按照(一)中的方法得出在组内只出现一次的数。
#define _CRT_SECURE_NO_WARNINGS 10
#include<stdio.h>
//返回值为右移后遇到1的位置。
size_t deffBitIndex(int num){
int i = 1;
int index = 0;
for (; i <= 32; i++){
if (num & 1){
return index;
}
index++;
num >>= 1;
}
return index;
}
int FindIndex(int num,int time){
int bl = 0;
for (int i = 1; i <= time; i++){
num >>= 1;
}
return num & 1;
}
//数组中只出现两次的具体解决函数
//list: 数据数组。
//num : 数组中数据的个数。
void FindTwoNum(const int* list, int num, int* x, int* y){
//数组元素依次异或得出的结果(结果为两个只出现一次的数的异或结果)
int xorResult = 0;
for (int i = 0; i < num; i++){
xorResult ^= list[i];
}
//该方法主要功能是计算xorResult的差异位。
size_t defBitIndexNum = deffBitIndex(xorResult);
for (int j = 0; j < num; j++){
//依据差异位将数组中的元素根据差异位与数据元素二进制表示下该位“按位与”的结果是否真进行分组,
//并直接根据组进行异或
if (FindIndex(list[j], defBitIndexNum)){
*x ^= list[j];
}
else{
*y ^= list[j];
}
}
}
//题目:找出数组中只出现两次的数
int main(){
//只出现一次的数为:3和9
int arr[] = { 9, 4, 2, 4, 8, 7, 2, 8, 7, 3 };
int sz = sizeof(arr) / sizeof(arr[0]);
int x = 0, y = 0;
//因为C语言无法返回两个变量,所以直接传址过去。
FindTwoNum(arr, sz, &x, &y);
printf("数字一:%d\n数字二:%d\n",x,y);
return 0;
}