消失的数字

63 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

前言~

有一道经典的面试题,是这样的:

数组nums包含从0到n的所有整数,但其中缺了一个。请完成下面的这个函数,以找出那个缺失的整数。你有办法在O(n)时间内完成吗? int missingNumber(int* nums, int numsSize) { }

如果题目没有规定要在O(n)时间内完成,那么这道题有很多解法,比如暴力的遍历,或者是先快速排序,然后再查找;但是这带来的问题就是,时间复杂度高于O(n)! 有没有简单的方法呢?有。

方法一:求差

这个方法非常的简单好懂:拿0到n这n+1个数的和与数组元素的和作差,得到的就是缺少的那个数。

int missingNumber(int* nums, int numsSize){
    int sum = 0, i;
    for(i = 0; i < numsSize; i++){
        sum  += nums[i];
    }
    return ((numsSize * (1 + numsSize))/2) - sum;
}

但是这种写法带来的问题就是:==可能会溢出==。

方法二:异或法

这近乎是这道题的最优解,但是理解起来有一定的难度,那么下面由我慢慢道来:

int missingNumber(int* nums,int numsSize)
{
	int i,x=numsSize;
	for(i=0;i<numsSize;i++)
		{
			x=x^nums[i]^i;
		}
	return x;
}

我们设置循环变量i来控制for循环的次数;x初始值设为numsSize;

因为我们知道,数组包含0到n这n+1个整数,所以最大的数为n,而数组缺少了其中一个,所以数组的大小为n,也就是说,最大数与数组大小都是n,也就是题目中的numsSize

我们设置了一个变量x,将其初始化为numsSize(为什么初始化为numsSize后面会讲);

然后我们利用一个for循环,遍历数组nums中所有的数字,让它跟x异或,然后再让i跟x异或,这样得到的值再赋给x,如此反复地进行异或操作......

当for循环结束时,我们应该知道的是,x跟i的所有可能值(0到numsSize-1)异或了,还跟nums数组中所有的元素异或了,也就是说,一开始的x=numsSize变成了: ==x=numsSize^0^1^2...^(numsSize-1)^nums[0]^nums[1]...^nums[numsSize-1]== 也就是: ==x=^0^1^2...^(numsSize-1)^numsSize^nums[0]^nums[1]...^nums[numsSize-1]==

我们知道,异或的规则是:对应二进制位相同为0,不同为1;

也就是说,两个相同的数异或得到0,并且任何数和0异或得到的还是这个数本身;

同时,异或遵循交换律和分配律,也就是说:a^b^c=a^c^b;

那么上面x的那串式子的意思就是:我们把数组中所有的数和0到numsSize之间的数 一 一 进行异或运算,怎么个 一 一 异或呢?

也就是:假设num[0]是1,那么它和1异或得到0,num[1]是5,那么它和5异或得到0,最终的结果就是:我们得到了numsSize个0,因为数组有numsSize个元素。

最后剩下了一个数找不到伴儿,它就是数组nums缺少的那个数,我们把它暂时叫做aim; 此时的x就变成x=0^0^0...^aim^0^0...^0;

而aim和0异或得到的还是aim,所以最终x=aim,于是我们把x返回就可以了。

这样也就能解释为什么x要初始化为numsSize了:因为我们想要0到numsSize之间的数(这里借助的是i)和数组元素进行异或,而循环变量i最大只能是numsSize-1,所以x要初始化为numsSize;

补充:

如何不使用临时变量实现a,b两个数值的交换?

a=a^b;
b=a^b;
a=a^b;

第一次令a=a^b,第二次b=a^b^b,也就是a,最后a=a^b^a,也就是b;

听懂了嘛?没懂得同学可以再看一看上面的解析,好好品味一下哦,同时也欢迎评论区提问。

==//博主定期更新C语言相关知识供小白们提高自己的能力,欢迎关注哦。==