ARTS 挑战打卡的第2天 --- Linux驱动与设备的匹配规则(Algorithm)

前言

(1)因为为了参加ARTS 挑战打卡,要求每周刷一道leetcode。作为一名嵌入式工程师,虽然也是搞软件的,但是对leetcode刷题方面强调的很少。
(2)毕竟还是做软件开发的,虽然嵌入式方向对这个强调比较少,还是刷一点简单的题目培养一下自己的思维。
(3)这到简单题做了我半个小时,最终击败5%的人。(苦笑)
(4)原题链接 : https://leetcode.cn/problems/missing-number-lcci/

题目

(1)数组nums包含从0到n的所有整数,但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗?
(2)示例1

输入:[3,0,1]
输出:2

(3)示例2

输入:[9,6,4,2,3,5,7,0,1]
输出:8

思路1

(1)最开始的思路,毫无疑问采用冒泡排序。将这个无序的数组变成有序数列。然后将这个数组相邻两项相减,如果不为1,就返回中间这个数据。
(2)当时我是这样写的,最后没想到,提交上去,他给我玩骚操作,数组只有一个[0],打我一个措手不及,最终输出结果要求是1,而我这里是-1。

int missingNumber(int* nums, int numsSize){
    int x,y,tmp;
    for(x=0 ; x<numsSize ; x++)
    {
        for(y=1 ; y<numsSize-x ; y++)
        {
            if(nums[y] < nums[y-1])
            {
                tmp = nums[y];
                nums[y] = nums[y-1];
                nums[y-1] = tmp;
            }
        }
    }
    for(x=1 ; x<numsSize ; x++)
    {
        if((nums[x] - nums[x-1]) != 1)
        {
            return nums[x] - 1;
        }
    }
    return -1;
}

(3)于是我更改策略,最后返回值是1。

int missingNumber(int* nums, int numsSize){
    int x,y,tmp;
    for(x=0 ; x<numsSize ; x++)
    {
		//...
    }
    for(x=1 ; x<numsSize ; x++)
    {
		//...
    }
    return 1;
}

(4)没想到,他又给我整出来了一波骚操作,输入数组为[1]。
(5)于是我就想如果数组只有一个值,就判断这个值是不是0。如果不是0就返回0,如果数组第一个值是0就返回1。

int missingNumber(int* nums, int numsSize){
    int x,y,tmp;
    for(x=0 ; x<numsSize ; x++)
    {
		//...
    }
    if(numsSize == 1 && nums[0] !=0)
        return 0;
    for(x=1 ; x<numsSize ; x++)
    {
		//...
    }
    return 1;
}

(6)然后没想到这串代码又出问题了,他给我[0,1]数组,最终返回的是一个1,但是要求的返回值是2。于是我知道,如果这个数组中间没有缺失整数,那么久就返回不存在与这个数组中,并且比他大的一个值。
(7)于是我继续调整,如果这个数组没有问题,就返回刚好比这个数组大1的值。

int missingNumber(int* nums, int numsSize){
    int x,y,tmp;
    for(x=0 ; x<numsSize ; x++)
    {
		//...
    }
	//...
    for(x=1 ; x<numsSize ; x++)
    {
		//...
    }
    return nums[x] + 1;
}

(8)之后就发现报错了。AddressSanitizer: heap-buffer-overflow on address
(9)这里涉及到一个C语言的小知识。首先执行表达式1,再执行判断表达式2,如果符合,则执行表达式4,否则,停止执行,最后执行表达式3。一开始我认为如果判断表达式2没有成功,表示式3和4都不会执行。现在才发现最基础的知识都搞错了。

for(表达式1;表达式2;表达式3){
  表达式4;
}

(10)因此,我又做了一次调整。让这个for循环的判断条件都改变一下。

int missingNumber(int* nums, int numsSize){
    int x,y,tmp;
    for(x=0 ; x<numsSize ; x++)
    {
		//...
    }
	//..
    for(x=0 ; x<numsSize-1 ; x++)
    {
        if((nums[x+1] - nums[x]) != 1)
        {
            return nums[x+1] - 1;
        }
    }
    return nums[x]+1;
}

(11)不出意外,又报错了。[1,2]的数组,实际上应该是要返回0的,但是我这里返回的是3。
(12)因此我做出了最后一次调整,只要排序之后的数组第一个值不是0,那么就会返回0。

int missingNumber(int* nums, int numsSize){
    int x,y,tmp;
    for(x=0 ; x<numsSize ; x++)
    {
		//...
    }
	if( nums[0] != 0)
		return 0;
    for(x=0 ; x<numsSize-1 ; x++)
    {
		//...
    }
    return nums[x]+1;
}

思路2

(1)虽然上面的代码成功运行了。但是通过复杂度计算,发现上面这个复杂度是O(N^2)。
(2)此时我就感觉方法不行,后面看了别人的思路之后,发现可以将所有的0到N加到一起为ret1,而数组的值都加起来, 是ret2。最终让ret1-ret2。

int missingNumber(int* nums, int numsSize){
    long int i,nums_sum=0,need_sum=0;
    for(i=0 ; i<numsSize ; i++)
    {
        nums_sum += nums[i];
        need_sum += i;
    }
    return need_sum - nums_sum;
}

(3)后面运行之后发现,结果永远是1。
(4)经过简单的思考之后发现,如果你是缺少了某一项,那么need_sum要比nums_sum多加一个数字,所以for结束之后,need_sum还需要进行一次加法。

int missingNumber(int* nums, int numsSize){
    int i;
    long int nums_sum=0,need_sum=0;
    for(i=0 ; i<numsSize ; i++)
    {
        nums_sum += nums[i];
        need_sum += i;
    }
    need_sum += i;
    return need_sum - nums_sum;
}

思路3

(1)正常来说,上面的方法已经很好了。但是我们是不是再换一个思路呢?
(2)我们需要知道C语言有一个东西,叫做异或^。如果一个数,被偶数次异或 ^,那么就会被消除。
(3)因此,我们可以让x进行异或操作,最终异或剩下来的值就是被忽略的值。
(4)这个方法相比较上面那个,节约了一点点空间,从时间复杂度上并没有提升。

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

总结

被偶数次异或 ^,相当于什么都没有发生。我们要积极利用这一条规则。

你可能感兴趣的:(#,数据结构,linux,算法,运维)