手摸手提桶跑路——LeetCode442. 数组中重复的数据

124 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第31天,点击查看活动详情

题目描述

给你一个长度为 n 的整数数组 nums ,其中 nums 的所有整数都在范围 [1, n] 内,且每个整数出现 一次两次 。请你找出所有出现 两次 的整数,并以数组形式返回。

你必须设计并实现一个时间复杂度为 O(n) 且仅使用常量额外空间的算法解决此问题

解题思路——标记法

原本是想用暴力一把梭,直接 hashmap 统计出现次数,输出所有出现次数大于 1 的整数,但是题目要求时间复杂度为 O(n) 且使用常量级的额外空间,那就莫得办法了。

后面想着说用 排序,这样出现两次的整数就会相邻了,但是排序的时间复杂度是 O(nlogn),也不满足题目的要求。

微信图片_20201123151920.png

如果只用常量的额外空间且时间复杂度为 O(n),我们大概率只能使用原地算法了,即在原数组上做文章。

我们仔细看看题目,给定的整数数组 nums 的长度为 n,且范围在 [1, n] 内,也就是说数组中的元素都是 大于0 的。

那么出现两次的整数要和出现一次的整数必须 有些区别 才可以。

我们可以将出现一次的整数,乘以 -1。这样一来,我们让出现一次的整数 n,变为 -n,让出现两次的整数 b,变为 -(-n)=n。这样就知道最后的数组中,如果是正数的,就说明这个数出现了两次,为负数的,说明只出现了一次。

那么思路有了,我怎么才能实现出现第二次的时候变为正数呢,拿 [1,2,1,3] 来说,当遍历到第二个 1 的时候,我咋知道它是第二个 1

微信图片_20201117133144.jpg

这里我们可以使用 映射 的方式。对于一个 [1, n] 的整数 n 来说,我们将 n-1 作为下标,让数组中下标为 n-1 的元素,乘以 -1。这时候有人跳出来问了,那我遍历到下标 n-1 的元素时,这个数都变成负数了,减 1 之后的下标你找给我看啊。

微信图片_20220819152846.jpg

所以刚刚说的并不全对,我们要将整数 n绝对值 之后再减 1

微信图片_20220818195830.jpg

如此一来,当一个整数被抓到第二次时,对应的下标是不是又变成正数了?

题解

var findDuplicates = function(nums) {
    const lens = nums.length, ans = [];

    for(let i=0; i<lens; ++i) {
        const index = Math.abs(nums[i]);
        if(nums[index-1] < 0) {
            ans.push(index);
        }
        nums[index-1] *= -1;
    }
    return ans;
}

微信截图_20220819153747.png