已知一个数组中,除了一个数字出现一次外,其他数字都出现两次,试找出这个数~~
思路分析:当看到这个题目,我就想,既然只有一个数出现一次,那么我们可以遍历这个数组,只要遇到相同的两
个数,就把它们置为一个比较大的数,最后输出那个没有被改变的数。
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
int arr[9];
int i = 0;
int j = 0;
for (;i < 9;i++)
{
scanf("%d",&arr[i]);
}
for (i = 0;i < 8;i++)
{
for (j = i + 1;j < 9;j++)
{
if ((arr[i] == (arr[i] & arr[j]) + ((arr[i] ^ arr[j]) >> 1)))
{
arr[i] = 32767;
arr[j] = 32767;
}
}
}
for (i = 0;i < 9;i++)
{
if (32767 != arr[i])
printf("%d",arr[i]);
}
system("pause");
return 0;
}
代码分析:
代码缺陷:代码时间复杂度为o(n^2),还有将相同的数分别置成32767,我们不能保证数组中原本就没有 这个 数。
代码亮点:使用了一种比较高端的方法求出两数的平均值,这样就能防止数据过大,两数之和发生溢出。
下边,我来重点解析这种求平均数的妙法~
求平均数的另一种方法:
a和b的平均数mid,mid = (a - b)/2+a;这种方法可以适用于任何类型数的平均数,而上述位运算的方法只能
适用于整形数,且两个数的符号位相同的那种。
学习编程这么久,我们发现,经常一个题目有好多种方法,我们应该择优,也就是,我们的目的不是解决问题,
而是更好的解决问题。上边求平均数的方法中,我们可以发现两个相同的数异或的结果是0,看看我们可否利用
这种方法解决呢??
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
int arr[] = {1,2,3,4,1,2,5,3,5};
int i = 0;
int ret = 0;
for (i = 0;i < sizeof(arr) / sizeof(arr[0]);i++)
{
ret ^= arr[i];
}
printf("%d",ret);
system("pause");
return 0;
}
上述方法很好的利用了异或运算符,时间复杂度是O(n)。由于代码比较少,所以没有必要写成函数。
如果一个数组中有两个数没有成对出现,其他数都成对,那么我们又是怎样找到这两个数呢??
如果我们能将两个不同的数分开,然后利用上述找一个数的方法也可以找出这两个数。我们将实现方法写成函数的
形式。看以下代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
void find_two_num(int arr[], int n, int *p1, int *p2)
{
assert(p1);
assert(p2);
int i = 0;
int ret = 0;//用来存储所有元素异或的结果
int pos = 0;
for (i = 0;i < n;i++)
{
ret ^= arr[i];
}
while (((ret >> pos) & 1) != 1)
{
pos++;
}
for (i = 0;i < n;i++)
{
if (((arr[i] >> pos) &1) == 0)
{
*p1 ^= arr[i];
}
}
*p2 = ret ^ *p1;
}
int main()
{
int arr[] = {1,2,3,4,1,2,3,5};
int num1 = 0;
int num2 = 0;
find_two_num(arr,8,&num1,&num2);
printf("%d %d",num1,num2);
system("pause");
return 0;
}
在以往的函数中,我们一般只需要一个返回值,而这个程序需要接受两个返回值,怎么办呢??利用返回型参数。
也就是传给函数的是存储数据的地址。
代码解析:我们的目标是将这个数组的元素分开,
比如4,5只出现一次,100^101 = 001,我们根据为1的那一位也就是第0位是否为1将数据分成两组,相同的数必
然被分到一组,不同的数必将位于不同的组,然后将每个组异或就可得到结果~~
我们没有必要将分的组的数保存起来,因为没有必要,只需要将组内的所有元素异或就行。
还有,我们只要求出一个组的单独出现的数,只需要将两个数异或的结果(即为代码中的ret)异或已经求出的
出现一次的数就可以~推导如下:
比如:a ^ b = m;
则: a ^ b ^a = m^a;
b = m ^a;
特别注意:位运算符 的优先级不太高(低于算术运算符的优先级),所以必要的时候不要吝啬括号哦~~
关于位运算符的强大作用,之后我再整理~~以上如有问题,希望大家指出~~