442. 数组中重复的数据

给你一个长度为 n 的整数数组 nums ,其中 nums 的所有整数都在范围 [1, n] 内,且每个整数出现 一次两次 。请你找出所有出现 两次 的整数,并以数组形式返回。
你必须设计并实现一个时间复杂度为 O(n) 且仅使用常量额外空间的算法解决此问题。
示例 1:
输入:nums = [4,3,2,7,8,2,3,1] 输出:[2,3]
示例 2:
输入:nums = [1,1,2] 输出:[1]
示例 3:
输入:nums = [1] 输出:[]
提示:
  • n == nums.length
  • 1 <= n <= 105
  • 1 <= nums[i] <= n
  • nums 中的每个元素出现 一次两次
通过次数74,317提交次数101,061

法1 原地hash

思路
按照
🕤
41. 缺失的第一个正数
思路,将每个数字放到 正确的位置,那最后不在正确位置的值必定就是重复的值。
例如:[4,3,2,7,8,2,3,1],都放到正确位置后,为[1, 2, 3, 4, 2, 3, 7, 8] 最后 i=4 和 i= 5 的位置上值不对,所以这两处的元素必定是重复的。
题解
class Solution: def findDuplicates(self, nums: List[int]) -> List[int]: res = [] # 将值放到正确的位置 for i in range(0, len(nums)): # 当前值的正确位置就是 idx idx = nums[i] - 1 while nums[idx] != idx + 1: nums[idx], nums[i] = nums[i], nums[idx] idx = nums[i] - 1 # 某位置上值不对,那么该值一定是重复的 for i in range(0, len(nums)): if nums[i] != i+1: res.append(nums[i]) return res

法 2 原地hash

思路
正常思路是开辟一个 等长的数组,然后在每个值正确的位置标记。
所谓正确的位置是这样的:4的正确位置是 3,3 的正确位置是2...
现在不能新开辟空间,那我们可以在原数组上标记,但是一旦标记之后,就会把原数组的值给弄丢,所以采取一种特殊的标记方式,就是给原数组的值取反,取反表示已经标记过了,为正表示还原标记。
题解
class Solution: def findDuplicates(self, nums: List[int]) -> List[int]: res = [] for i in range(0, len(nums)): tag = abs(nums[i]) - 1 # 已经被标记过了 if nums[tag] < 0: res.append(abs(nums[i])) else: nums[tag] = - nums[tag] return res