【C语言刷题】Leetcode268丢失的数字

Leetcode268——丢失的数字

题目描述

给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。

题目链接:Leetcode268 丢失的数字

示例

示例 1
输入:nums = [3,0,1] 输出:2
解释:n = 3,因为有 3 个数字,所以所有的数字都在范围 [0,3] 内。2 是丢失的数字,因为它没有出现在 nums 中。

示例 2
输入:nums = [0,1]
输出:2
解释:n = 2,因为有 2 个数字,所以所有的数字都在范围 [0,2] 内。2 是丢失的数字,因为它没有出现在 nums 中。

示例 3
输入:nums = [9,6,4,2,3,5,7,0,1]
输出:8
解释:n = 9,因为有 9 个数字,所以所有的数字都在范围 [0,9] 内。8 是丢失的数字,因为它没有出现在 nums 中。

示例 4
输入:nums = [0]
输出:1
解释:n = 1,因为有 1 个数字,所以所有的数字都在范围 [0,1] 内。1 是丢失的数字,因为它没有出现在 nums 中。

提示

  • n == nums.length
  • 1 <= n <= 104
  • 0 <= nums[i] <= n
  • nums 中的所有数字都独一无二

核心代码模式

//nums是目标数组的指针,numsSize是目标数组元素个数
//返回丢失的数字
int missingNumber(int* nums, int numsSize)
{

}

思路分析和代码实现(C语言)

1.排序法

一个简单的做法是直接对nums进行排序,因为数组nums包括了[0,n]之间n - 1个数,就少了一个数并且每个数不会重复出现,比如说排了升序,排好序后每个下标都和对应数组的值相同,除了那个丢失的数字,随后遍历数组一个一个比对,若 nums[i] != i ,则 i 即为丢失的数字。

代码实现

int CmpInt(const void* e1, const void* e2)
{
    return *(int*)e1 - *(int*)e2;
}

int missingNumber(int* nums, int numsSize)
{
    int i = 0;
    int ret = 0;
    qsort(nums, numsSize, sizeof(int), CmpInt);
    
    for(i = 0; i < numsSize; i++)
    {
        if(nums[i] != i)
            ret = i;
    }
    
    return ret;
}

2.哈希数组法

额外创建一个数组cnt,用来记录各个数出现的次数,用它的下标对应某数字,用数组的值对应某数字出现的次数,比如cnt[3]的值为1表示原数组nums中3出现过一次。
第一次遍历数组,nums数组中出现过的数在cnt中全部置为1。
第二次遍历就是为了找出在0~numsSize内没被置为1的下标,即为丢失的数字。

代码实现

int missingNumber(int* nums, int numsSize)
{
    int *cnt = (int*)malloc(sizeof(int)*(numsSize+1));
    int i = 0;
    int ret = 1;//应对nums数组中只有一个0的情况

    memset(cnt, 0, sizeof(int)*(numsSize+1));//初始化数组全为0

    for(i = 0; i < numsSize; i++)
    {
        cnt[nums[i]] = 1;
    }

    for(i = 0; i <= numsSize; i++)
    {
        if(cnt[i] == 0)
            ret = i;
    }

    return ret;
}

3.原地哈希法

何谓“原地”?相比于上面的哈希数组法,这个思路不需要额外创建数组,直接在原数组nums上进行“哈希”。
相比于创建哈希数组,该思路中用原数组nums,只有numsSize-1个位置,要注意不要越界。先遍历一遍数组,如果下标和数组值对应不上(不相等)并且数组值不为numsSize(因为用numsSize做下标会越界),那就把nums[nums[i] ]和nums[i]的值进行交换,这样一换至少让nums[nums[i] ]和下标nums[i]对应上了,也就是每次交换至少让一个数回到下标对应处,交换完后记得让i自减1,假设i为2,自减1后为1,这一轮循环结束了会执行i++,i又变回了2,这样的话等到下一轮循环的时候还是先前的值,这是为了再检查先前交换完后该位置的值是不是和下标对应上了,如果是的话不会再交换,直接进入下一轮循环,如果不是的话,就要再交换。

void swap(int *nums, int x, int y)
{
    int tmp = nums[x];
    nums[x] = nums[y];
    nums[y] = tmp;
}

for(i = 0; i < numsSize; i++)
{
    if(nums[i] != i && nums[i] < numsSize)
    {
        swap(nums, nums[i], i);
        i--;
    }           
}

举个例子

假设数组nums元素为9, 6, 4, 2, 3, 5, 7, 0, 1, 第一轮循环遇到9,不满足image.png

原地“哈希”以后,我们要找出哪一个下标对应的数组值不相同是吗?实际上不完全是这样的,为什么?
举个简单的例子,假设数组给的是0, 1也就是少了2,你会发现下标都对应上了啊,但是怎样返回2啊,注意到没有,这个2就是numsSize,也就是说,在这种情况下我们就numsSize的值就好啦。
其实是有两种情况的:一是数组中存在numsSize,二是不存在numsSize
第一种情况下,numsSize压根就没有对应的数组下标,因为原数组只有numsSize-1个位置啊,此时其他元素在原地“哈希”后都能对应到下标,只有numsSize和下标不同,不过它的下标就是丢失的数字(因为丢失了所以这个位置就被numsSize占据了嘛)。所以只需要再遍历一遍数组,找到某元素和下标不相同,这个下标就是要找的数字,找到就返回。
第二种情况下,所有元素在原地“哈希”后都能和下标对应上,就比如0, 1, 2, 3缺少4,这时候丢失的数字就是numsSize嘛,直接返回它就好了。
(相似的可以用原地哈希解题的还有Leetcode448:找到所有数组中消失的数字)

代码实现

void swap(int *nums, int x, int y)
{
    int tmp = nums[x];
    nums[x] = nums[y];
    nums[y] = tmp;
}

int missingNumber(int* nums, int numsSize)
{
    int i = 0;
    int ret = 0;
    int flag = 0;

    for(i = 0; i < numsSize; i++)
    {
        if(nums[i] != i && nums[i] < numsSize)
        {
            swap(nums, nums[i], i);
            i--;
        }           
    }
    
    for(i = 0; i < numsSize; i++)
    {
        if(nums[i] != i)
            return i;
    }
        
    return numsSize;
}

4.位运算异或法

这个方法就有点厉害了,形象点来说就是一个萝卜一个坑,萝卜就是数组元素,坑就是下标。
首先,异或有如下两个规律
1.一个数异或它自身结果为0,如4^4==0
2.一个数异或0结果为它本身,如13^0==13
同时异或还具有交换律1^2^4^2^1相当于1^1^2^2^4也就是0^0^4即得到4。
定义一个变量,赋初始值为numsSize,让它不断和下标与数组元素异或(相同元素全部互相异或化为0,与0异或不影响数值),它和下标异或就相当于“挖坑”,和数组元素异或就相当于“填坑”,数组nums是缺失了一个数字的,也就是说最后会有一个“坑”填不上,那它对应的数字就是丢失的那个数字了。

代码实现

int missingNumber(int* nums, int numsSize)
{
    int ret = numsSize;
    int i = 0;

    for(i = 0; i < numsSize; i++)
    {
        ret ^= i;
        ret ^= nums[i];     
    }

    return ret;
}

5.作差法

数组nums就比[0,n]区间少了一个数对吧,那数组元素之和与0~n元素之和相比也就少了那一个数对吧,直接作差不就得出少了哪个数嘛。

代码实现

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

感谢观看,你的支持就是对我最大的鼓励~

你可能感兴趣的:(C语言刷题,c语言,算法,数据结构)