继续进行今天的每日一题的:
面试题40:
题目:一个整形数组里出了两个数字之外,其他的数字都出现了两次。请从程序中找出这两个只出现一次的数字。要求时间的复杂度为O(n),空间复杂度是O(1)。
这一道题,比如我们给出一个数组{2,3,4,5,6,2,3,4}我们按照最先开始的思维,会这样想,我从头进行遍历,对一个数字和它后续的数字进行匹配。如果后续有和他一样的数字,那么就看下一个数,如果后续没有和他一样的数,那么就把它输出来。这样的确能做出来这一道题,但是这样的代价就是时间复杂度要高很多。
所以这样一道题,会难住不少的面试者。所以在这里我们要转换思路,比如当只有一个数字只出现一次的时候,你怎么找出来它。
在这里我们就发散思维,我们可以想到异或运算的一个性质,当任何一个数字异或自己都是0。也就是说,我们对只含有一个只出现一次的数字的一个数组从头到尾进行异或,最后得到的结果最后肯定是那个只出现一次的数字。那么我们可以联想,对于有两个只出现一次的数字呢?
答案当然是这两个只出现一次的数字的异或结果啦!!
所以我们也就通过这样的简单分析也就能有一个清晰的思路了。
在这里我们首先对这个数组进行异或,然后通过结果把这个数组分成两个数组,在这两个数组中我们只放入一个只出现一次的数,这样,在对这两个子数组进行异或,最后就回到了我们最初的一种思路,异或出来的结果就是最后只出现一次的那个数了。
接下来就是我们对思路进行实现的过程。
首先我们对这个数组进行异或,最后异或完成以后,我们要进行思考,怎么对这个数组进行分割成两个子数组呢?由于这两个只出现一次的数字不一样所以他们的结果肯定至少有一位有1,所一我们刚好可以以这个为标准,对这一位为1的分成一个组,不为1的分成一个组。是不是觉得一下子豁然开朗呢!!编程的乐趣就在这里吧!接下来对这两个子数组再进行异或,最后的结果就是我们的输出了。
在这里我也想提醒一下大家,思路明确以后,大家最好在纸上写一写程序,然后全部修改好以后,再在编译器尝试,或者可以在向sublime_text这样的代码编辑器上进行写代码,毕竟,咱们有笔试,面试也不会给你编译器,不要养成依赖编译器的习惯!
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
int judge_1(int n, int bit)
{
n = n >> bit;
return (n & 1);
}
void find_number( char* arr, int sz, int *num1, int* num2)
{
int bit = 0;
assert(arr);
int ret = 0;
int i = 0;
int j = 0;
for (i = 0; i < sz; i++)
ret = ret^arr[i];
while (((ret & 1) == 0)&&(bit < 32))//在这里我们来寻找最右边是1的位。
{
ret >>= 1;
bit++;
}
*num1 = *num2 = 0;
for (j = 0; j < sz; j++)
{
if (judge_1(arr[j], bit))//这里判断从右边数起你的bit那一位是不是1
{
*num1 ^= arr[j];//将是1的异或得出结果
}
else
*num2 ^= arr[j];//将不是1的异或得出结果
}
}
int main()
{
int num1 = 0;
int num2 = 0;
char arr[] = { 0,1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6 };
int sz = sizeof(arr) / sizeof(arr[0]);
find_number(arr,sz,&num1,&num2);
printf("%d,%d", num1, num2);
system("pause");
return 0;
}