面试 | 消失的数字【求和相减 + 异或位运算 + 哈希表】

在这里插入图片描述

在这里插入图片描述

一、题目描述

原题传送门

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

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

示例 2:

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

  • 本题的题目思路很简单,就是给你一个数组,然后让你输出连续的数组中消失的那一个数字。本题我给出两种解法

二、方法1:求和相减【C语言】

1、思路分析

首先我们来看看使用求和相减法的思路是怎样的

  • 这个思路不难想到,只需要运用一个等差数列的求和公式求和从0到n的总和,然后用这个总和值减去题目给出的数组数字的总和,无论应对哪一个案例,这个方法都是适用的

2、整体代码展示

我们来看一下代码

int missingNumber(int* nums, int numsSize){
    //1.先使用等差数列求和公式求出总的和
    int ret = (0 + numsSize) * (numsSize + 1)/ 2;
    	  						//项数:尾项 - 首项 + 1 = numSize - 0 + 1
    //2.使用循环遍历这个数组,题目给出的所有输出
    for(int i = 0;i < numsSize; ++i)
        //3.实行一个作差,找出缺少的那一个数
        ret -= nums[i];

    //4.返回的结果就是缺失的那个数字
    return ret;
}

3、代码详解

然后我们分步来讲解一下

  • 首先第一步,使用一个等差数列的求和公式,求出从0到numSize所有数字的总和,放入变量ret中
  • 然后第二步,使用循环遍历一下整个数组,用上面求出的ret减去题目给出的nums数组中的每一个数字
  • 最后第三步,返回的结果即为消失的那个数组

可以看出,本思路非常简单,这个大家应该是都可以想到,接下去我们来看第二个思路

三、方法2:异或位运算【C语言】

1、思路分析

  • 异或这种位运算,不知道大家之前在学习C语言的时候有没有注意,如果忘记了就再去看一下吧 – >异或位运算
  • 整体的思路大概是声明一个变量,将其初始化为0,然后和题目中给出的所有数字先以后一遍,接着再用异或完的数据与0~N的数字再进行一个异或,最后变量中保存的数字便是消失的那个数字

2、整体代码展示

先看一下整体的代码

/*
    异或规则:
    a ^ a = 0   【两个相同的数异或一定为0】
    a ^ 0 = a   【任何数和0异或都是它本身】
*/
int missingNumber(int* nums, int numsSize){
                                    //3
    int N = numsSize;
    int x = 0;          //3
    for(int i = 0;i < numsSize; ++i)
    {
        x ^= nums[i];
        //x ^ 3 ^ 0 ^ 1
    }
                        //4
    for(int i = 0;i < N + 1; ++i)
    {
        x ^= i;
        //---> x ^ 3 ^ 0 ^ 1 ^ 0 ^ 1 ^ 2 ^ 3
        //---> x ^ 0 ^ 0 ^ 1 ^ 1 ^ 3 ^ 3 ^ 2
        //---> x ^ 2 ==> x = 2(任何数和0异或都是它)
    }  
    return x;
}

3、代码详解

由于下面要用到异或的规则,所以先给出规则的定义

  1. 所有数字和0异或都是它自己本身
  2. 所有数字和它自己异或都是0
  3. 异或运算具备交换律和结合律

然后我们来分析一下这段代码以及解释一下这个异或的逻辑【为了能一步步顺理,所以做成长图】

在这里插入图片描述

  • 可以看到,本题的思路是首先定义一个变量x,将其初始化为0,这个初始化的0和我们平常在初始化其他变量时使用到的0可是有区别的,因为这里要使用到异或的规则——> 任何数和0异或都是它本身
  • 所以在一开始先去和题目中给出的数组中的数字去进行一个异或,保留下它们,然后再从从0到numSize的所有数字去进行异或,此时就可以发现出现了很多相同的数字,再根据异或的规则就可以知道——> 两个相同的数字相异或为0,那么出现了的数字就会被异或掉,而消失掉的那个数字就只会存在一个,那么便会被保存下来,然后这个数字再和x也就是0去进行异或,那么最后x里面存的也就是消失的那个数字

如果对以上逻辑有疑惑,可以到编译器里自己调试一下,就可以很清楚了

四、方法3:哈希表【C++】

1、思路分析

然后最后一种思路呢,是我近阶段才看到的一种方法,觉得可不错,也分享给大家,主要是使用到高阶数据结构中的【哈希表】

  • 整体的思路大概是将题目中给出的数字先放入哈希表中,然后再去遍历从0~n的所有数字,去哈希表中寻找是否有与其相同的数字,若是在遍历的过程中发现有数字不存在,则说明这个就是消失的那个数字,记录下这个数字跳出循环的遍历,返回这个数字即可

2、整体代码展示

然后展示一下代码

class Solution {
public:
    int missingNumber(vector<int>& nums) {
        unordered_set<int> set;
        int n = nums.size();

        //1.首先将题目给出的数字放入哈希表中
        for(int i = 0;i < n; ++i)
            set.insert(nums[i]);

        int missing = -1;       //消失的数字
        
        //2.然后遍历从0~n的所有数字,去哈希表中寻找是否有与其相同的数字
        for(int j = 0;j <= n; ++j)
        {
            //3.若是发现有数字没有在表中,则表明此为消失的数字
            if(!set.count(j))
            {
                //4.记录这个消失的数字
                missing = j;
                break;      //已经找到这个数字,跳出循环的遍历
            } 
        }
        return missing;
    }
};

3、代码详解

然后我们对代码来解说一下,逻辑并不复杂,一步步下来就行

  • 首先呢我们需要去定义一个哈希集合,用来存放题目中给出的数字
  • 接着就是将题目中给出的所有数字放入哈希集合中,这里使用的是一个API【insert()】
  • 其次我们要定义一个变量用于保存那个消失的数字,然后遍历从0~n的所有数字,去哈希表中寻找是否有与其相同的数字。这里使用的是一个API【count()】,作用是统计当前的这个数字是否又在哈希表中出现过,取反【!】表示的就是没有出现的数字,若是符合,则使用missing变量去记录这个数字,然后break跳出循环,因为已经找到了和这个消失的数字就不要再寻找了
  • 最后返回即可

不过对于哈希表来说,虽然是可以AC,但是呢由于哈希表需要维护数组,因此空间复杂度为O(N),不过题目要求在O(N)的时间复杂度内完成,哈希表起到的其实就是一个空间换时间的操作,对于时间的话在搜索算法中可以当仁不让的O(1)

五、总结与提炼

  • 本文我们主要是讲解了一道面试题,叫做【消失的数字】,面对题目给出的数组,查找出中间消失的那个数字
  • 在本文中,主要是给出了三种解法【求和相减】、【异或位运算】、【哈希表】,三种方法各有不同,分享出来希望你都可以掌握,多学会一种解法总是好的

以上就是本文的所有内容,如有问题请于评论区留言或者私信我,感谢您对本文的观看🌺

在这里插入图片描述

  • 12
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 14
    评论
根据题意,我们需要证明 ||x-y||=||x+y||,其中 x⊥y。 根据内积的定义,我们有: ||x+y||^2 = ⟨x+y,x+y⟩ = ⟨x,x⟩ + ⟨x,y⟩ + ⟨y,x⟩ + ⟨y,y⟩ = ||x||^2 + 2Re⟨x,y⟩ + ||y||^2 (其中Re表示实部) 同理,有: ||x-y||^2 = ⟨x-y,x-y⟩ = ⟨x,x⟩ - ⟨x,y⟩ - ⟨y,x⟩ + ⟨y,y⟩ = ||x||^2 - 2Re⟨x,y⟩ + ||y||^2 因为 x⊥y,所以 ⟨x,y⟩=0,代入上面两个式子得: ||x+y||^2 = ||x||^2 + ||y||^2 ||x-y||^2 = ||x||^2 + ||y||^2 两式相减得: ||x+y||^2 - ||x-y||^2 = 4Re⟨x,y⟩ 因为 Re⟨x,y⟩≤|⟨x,y⟩|,所以有: ||x+y||^2 - ||x-y||^2 ≤ 4|⟨x,y⟩| 又因为 Hölder 不等式有: |⟨x,y⟩| ≤ ||x||·||y|| 所以有: ||x+y||^2 - ||x-y||^2 ≤ 4||x||·||y|| 两边同时开方得: ||x+y|| - ||x-y|| ≤ 2||x||·||y|| / ||x+y|| 因为 ||x+y||≠0,所以有: ||x-y|| / ||x+y|| ≤ 2||x||·||y|| / ||x+y||^2 两边同时乘以 ||x+y||,得: ||x-y|| ≤ 2||x||·||y|| / ||x+y||·||x+y|| 因为 ||x||和||y||都是非负数,所以有: ||x-y|| ≤ 2||x||·||y|| / ||x+y||^2 ≤ 2||x||·||y|| / 4min(||x||^2,||y||^2) 因为 x⊥y,所以 ||x||^2+||y||^2≠0,所以有: ||x-y|| ≤ 2||x||·||y|| / (||x||^2+||y||^2) 同理可得: ||x+y|| ≤ 2||x||·||y|| / (||x||^2+||y||^2) 因为 ||x||和||y||都是非负数,所以有: ||x-y||·||x+y|| ≤ 4||x||^2·||y||^2 / (||x||^2+||y||^2) = 4||x||^2·||y||^2 / (||x||^2+||y||^2) - ||x||^2 + ||x||^2 + ||y||^2 = ||x||^2 + 2||x||·||y|| + ||y||^2 - ||x||^2 + ||x||^2 + ||y||^2 = 4||x||·||y|| 两边同时开方得: ||x-y||·||x+y|| ≤ 2||x||·2||y|| 因为 x⊥y,所以 ||x||·||y||=0,所以有: ||x-y||·||x+y|| = 0 因为 ||x-y||和||x+y||都是非负数,所以有: ||x-y|| = ||x+y|| 因此,证毕。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

烽起黎明

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值