提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
这道题主要要让我们求出在一个数组中只出现1次的两个数,下面有两种解题思路一种是利用排序算法,把整个数列排成一个有序数列在两两进行比较,还有一种是利用异与的方式进行,本片重点讲异与方式。
一、程序实现
# include<stdio.h>
void find_number(int* arr, int sz,int* num1,int* num2)
{
int k = 0;
int temp = 0;
for (int i = 0; i < sz; i++)
{
temp = temp ^ arr[i];//得到^的结果。
}
for (int i = 0; i < 32; i++)
{
if(((temp >> i)&1) != 0)
{
k = i;//得到k是第几位.
break;
}
}
*num1 = 0;
*num2 = 0;
for (int j = 0; j < sz; j++)
{
if(((arr[j]>>k)&1) == 1)
{
*num1 =*num1^ arr[j];
}
else
{
*num2 = *num2 ^ arr[j];
}
}
}
int main()
{
int arr[] = { 1,2,3,4,5,6,1,2,3,4,5,7 };
int sz = sizeof(arr) / sizeof(arr[0]);
int num1 = 0;
int num2 = 0;
find_number(arr,sz,&num1,&num2);
printf("%d %d\n", num1, num2);
return 0;
}
还有一种方式是用冒泡排序先排好之后,利用两两之间进行比较如果两两之间比较的数相同,则就进行+2跳过本次比较,如果两两比较不同则就证明这个数是单身狗打印输出数据,然后在自增1,整体看用排序方法是很容易想到的。
下面是代码,本篇主要说的是异与的方法这种排序方法就简略跳过。
# include<stdio.h>
void bubble_number(int* arr, int sz)
{
for (int i = 0; i < sz - 1; i++)
{
for (int j = 0; j < (sz - 1) - i; j++)
{
int temp = 0;
if (arr[j] > arr[j + 1])
{
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
void find_number(int* arr, int sz)
{
for (int z = 0; z < sz;)
{
if (arr[z] == arr[z + 1])
{
z = z + 1;
}
else
{
printf("%d ", arr[z]);
z++;
}
}
}
int main()
{
int arr[] = { 1,2,3,4,5,1,2,3,4,6 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_number(arr, sz);
find_number(arr, sz);
return 0;
}
运行结果为;
二、分布讲解
1.大体思路讲解
大体思路是把这个数组里的两个单独的数给分到两个数组里,在把这两个数组里数分别进行异与,得到的结果就是这两个单独的数,流首先我们要知道一个异与的重要公式a^a=0,异与的计算方法为两个异与的二进制数进行比较如果两个数的对应二进制位相同,那么得到的结果就是0,如果对应的二进制位不同那么得到的结果就是1列如5和6进行异与5的二进制位是0101,6的二进制位是0110那么异与的结果是0011。本篇讲的方法就是用了这个异与的公式。下面将代码与文字相结合进行详细的讲解
2.详细讲解
代码如下:
int main()
{
int arr[] = { 1,2,3,4,5,6,1,2,3,4,5,7 };
int sz = sizeof(arr) / sizeof(arr[0]);
int num1 = 0;
int num2 = 0;
find_number(arr,sz,&num1,&num2);
printf("%d %d\n", num1, num2);
return 0;
}
这里是主函数,就不过多讲解了,就是一些简单的定义啥的,接下来进入find_number函数。
void find_number(int* arr, int sz,int* num1,int* num2)
{
int k = 0;
int temp = 0;
for (int i = 0; i < sz; i++)
{
temp = temp ^ arr[i];//得到^的结果。
}
这一段主要就是把数组里面的所有数都进行异与,最终得到的数就是单独的两个数异与的结果因为两个相同的数异与得到的数就是0,那么为什么要把整个数组里面的数进行异与呢,那是因为我们需要把这两个不同的数分到两个不同的数组里面去,就向这样1,1,2,2,3,3,6,为一组,4,4,5,5,7为一组,那我们怎么分呢?就是用到异与的方法最终得到的二进制数列必然有一我们就拿5和6进行举列他们相异与得到0011,这个结果就证明两个二进制数列的第1,2位是不同的!这里很关键!我们就需要找到在整个数列相异与之后,这个二进制数列何时,哪一位有1我们就需要抓住这个1进行分成两段数,还是拿5和6举列他们相异与的结果是0011,这里的第一位是1,我们就抓住这个1进行分类5的二进制是0101,6的二进制位是0110.这样就可以轻松的把6和5分到不同的组里去了,下面是代码。
for (int i = 0; i < 32; i++)
{
if(((temp >> i)&1) != 0)
{
k = i;//得到k是第几位.
break;
}
}
*num1 = 0;
*num2 = 0;
for (int j = 0; j < sz; j++)
{
if(((arr[j]>>k)&1) == 1)
{
*num1 =*num1^ arr[j];
}
else
{
*num2 = *num2 ^ arr[j];
}
}
}
这里的第一个for循环就是找1的过程,为啥要i小于32,因为int的最大二进制是32位,最坏的结果就是1在第32位上,这里用了右移操作符和1进行按位与运算,按位与就是当两个二进制位都是1时得到的答案就是1,不相同就是0,当找到1之后就立即跳出循环就得到k,k表示在第几位上有1。然后令num1和num2等于0,这么做要保险一点,因为在进入下一个循环里面时我们必须保证这两个数是0,这样才有意义
在下面就是把数组里面的数进行右移K位与1进行按位与运算看看等不等于1,目的是把两个单独出现的的数分开,如果等于就把他放到同一组里面,否则是0就放到另一组数里面。进行异与,最终得到的结果就是两个单独出现的数字。
总结
总的来说这两种方法都可行的,但第一种更有意思一些,也富有挑战性