[LeetCode] Missing Number

本文介绍了寻找数组中缺失数字的三种高效算法:求和法、异或法及二分查找法,并提供了C++实现代码。

前言

Missing Number是一道和Single Number很像的题(无论是从名字还是解法的角度而言),具体思路并不难,做法倒是不少。

题目

https://leetcode.com/problems/missing-number/
Given an array containing n distinct numbers taken from 0, 1, 2, …, n, find the one that is missing from the array.
For example,
Given nums = [0, 1, 3] return 2.
Note:
Your algorithm should run in linear runtime complexity. Could you implement it using only constant extra space complexity?
说是给出n个数字,范围为0到n且不重复,找出那个没有出现的数字。(0-n共有n+1个数字)

思路

首先可以想到的做法就是求和。显而易见,这是个(如果Missing Number并没有Miss的话)等差数列,由0到n,中学数学知识告诉我们,它们的和是

sum = (0 + n) * (n + 1) / 2

那么只要从这个和中减去所有出现的n个数字,最后得到的就是那个Missing Number。这个思路最为直接,代码也最为简洁。

第二种做法,灵感来源于著名的Single Number。给出的数组是0-n之间的n个数字,而下标(index)则是从0一直到n-1。我们可以设想,这个Missing Number在数组中,也就意味着数组下标是0到n,那么这种情况下,下标(index)和数值(array element)是完全可以一一对应起来的。那么如果把下标的集合和元素的集合拼成一个集合,在这个集合中每个数字都将出现两次,但是Missing Number将只出现一次(只在下标集合中出现而不在元素集合中出现)。这时,Missing Number其实就是个Single Number了。
我们从0到n异或一遍,再把所有元素,即nums[0]到nums[n-1],全部异或一遍,最终就可以得到那个Missing的家伙。

第三种做法,也是很自然的思路。既然要找一个数字,那么我们很容易会想到经典的二分查找(Binary Search)。值得一提的是,这也是我在高考后的那个暑假看到的Harvard CS入门课程中介绍的第一个算法,因为它实在是太经典,太自然了。
在这道题中,我们先排序这个数组,然后不断二分查找,就可以轻而易举的找到Missing Number。关键语句是 nums[mid] > mid ,我们知道,排序后没有Missing Number的话,对于每个i,number[i] == i 都成立。而如果nums[mid] > mid,说明接下来应该排查mid左侧的部分(right = mid),否则排查右侧的部分(left = mid + 1)。最终就能锁定Missing的是Left。

代码

Solution 1 求和

class Solution {
public:
    int missingNumber(vector<int>& nums) {
      int len = nums.size();
      int sum = (0 + len) * (len + 1) /2;
      for (int i = 0; i < len; i++) {
        sum -= nums[i];
      }
      return sum;
    }
};

Solution 2 异或

class Solution {
public:
    int missingNumber(vector<int>& nums) {
      int res = 0;
      for (int i = 0; i < nums.size(); i++) {
        res ^= nums[i];
        res ^= i;
      }
      res ^= nums.size();
      return res;
    }
};

Solution 3 二分查找

class Solution {
public:
    int missingNumber(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int left = 0, right = nums.size(), mid;
        while (left < right) {
          mid = (left + right) /2;
          if (nums[mid] > mid) 
            right = mid;
          else 
            left = mid + 1;
        }
        return left;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值