leetcode每天一题:【找到所有数组中消失的数字】(简单)

430 阅读2分钟

这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战

题目描述

leetcode题目地址

一个数组,包含n个整数,整数的范围是在 [1,n] 之间,然后要找出在这个范围但是没有在数组里面的数,然后以数组的形式返回。

举个例子:

数组:[2,1,3,2,1]
返回:[4,5]  n是5,然后45不在数组内,则返回

数组:[2,1,2]
返回:[3]   n是3,然后3不在数组内,则返回 

思路分析

第一种方法

这个题目可以从n这里入手,因为它是从1到n的数中找到数组不存在的数,则我们可以从1遍历到n,然后一个一个去数组中判断是否存在,如果存在则不处理,不存在则存到新数组中,最后返回新数组。

代码如下:

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var findDisappearedNumbers = function (nums) {
  const arr = []
  for (let i = 1; i <= nums.length; i++) {
    if (!nums.includes(i)) arr.push(i)
  }
  return arr
};

这个代码不长,看着简单,但是这个方法时间复杂度是O(n^2),因为你在for遍历里面又用了数组的includes方法来判断,includes也相当于一次遍历。

这样的结果就非常耗时,执行耗时要6秒多

image.png

第二种方法

下面来看另外一种方法

既然遍历嵌套遍历会导致复杂度变大,那么我们可以使用对象来做映射,这样遍历就不用嵌套了。首先先把1到n的数存到对象中,value都是true

然后再次遍历数组,判断对象是否存在数组的值,如果存在,则把它删除。

最后对象剩下的就是数组没有数字,获取key,返回就可以了。

代码如下:

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var findDisappearedNumbers = function (nums) {
  var obj = {}
  for (let i = 1; i <= nums.length; i++) {
    obj[i] = true
  }
  for (let i = 1; i <= nums.length; i++) {
    if (obj[nums[i - 1]]) delete obj[nums[i - 1]]
  }
  return Object.keys(obj).map(num => +num)
};

这个方法比上面的方法好一点,时间复杂度降了,执行用时也变少了

image.png

第三种方法

上面的两种方法我们是用了额外空间,是否可以在不使用额外空间的情况下同时一次遍历就可以?

答案是可以的。

因为这个数组的长度是n,然后里面的值不会大于n,所以可以利用对数组的值减1后用n取余当作索引(因为值是从1开始的,减1,代表索引可以从0开始)。然后对这个索引的值加n。没有出现的索引,则它对应的值不会大于n。

然后再遍历数组,如果发现当前值小于等于n就证明当前索引是不在数组中,则把它push到新数组中,最后返回新数组。

代码如下:

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var findDisappearedNumbers = function (nums) {
  const arr = []
  const n = nums.length
  for (let i = 0; i < n; i++) {
    const x = (nums[i] - 1) % n
    nums[x] += n
  }
  for (let i = 0; i < n; i++) {
    if (nums[i] <= n) arr.push(i + 1)
  }
  return arr
};

确实妙~

这个方法比前面两个方法更快了,耗时更少,同时时间复杂度没有增加,就是不好理解,大家可以试试。

image.png