找出一个数组中的”单身“

来源:互联网 发布:省流量的软件 编辑:程序博客网 时间:2024/05/16 04:57

已知一个数组中,除了一个数字出现一次外,其他数字都出现两次,试找出这个数~~

思路分析:当看到这个题目,我就想,既然只有一个数出现一次,那么我们可以遍历这个数组,只要遇到相同的两

                   个数,就把它们置为一个比较大的数,最后输出那个没有被改变的数。

#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;

特别注意:位运算符 的优先级不太高(低于算术运算符的优先级),所以必要的时候不要吝啬括号哦~~

   关于位运算符的强大作用,之后我再整理~~以上如有问题,希望大家指出~~

0 0
原创粉丝点击