找出一个数组中只出现一个的数
已知一个数组中,除了一个数字出现一次外,其他数字都出现两次,试找出这个数~~
思路分析:当看到这个题目,我就想,既然只有一个数出现一次,那么我们可以遍历这个数组,只要遇到相同的两
个数,就把它们置为一个比较大的数,最后输出那个没有被改变的数。
- #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;
- }
如果一个数组中有两个数没有成对出现,其他数都成对,那么我们又是怎样找到这两个数呢??
如果我们能将两个不同的数分开,然后利用上述找一个数的方法也可以找出这两个数。我们将实现方法写成函数的
形式。看以下代码:
- #define _CRT_SECURE_NO_WARNINGS 1
- #include<stdio.h>
- #include<assert.h>
- void find_two_num(int arr[], int n, intint *p1, intint *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;
特别注意:位运算符 的优先级不太高(低于算术运算符的优先级),所以必要的时候不要吝啬括号哦~~