题目
给你一个长度为 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.length1 <= n <= 1051 <= nums[i] <= nnums中的每个元素出现 一次 或 两次
思路
解法一:原地交换元素+哨兵0【自】
遍历数组,如果 nums[i]!=i+1,原地交换元素,如果遇到nums[i]=nums[nums[i]-1],将nums[i]设置为哨兵变量0,然后将nums[i]加入结果中。
代码一:原地交换+哨兵0
class Solution:
def findDuplicates(self, nums: List[int]) -> List[int]:
res = []
n = len(nums)
for i in range(n):
if nums[i] == i + 1 or nums[i] == 0:
continue
while nums[i] != i + 1 and nums[i] != 0:
j = nums[i]
if nums[i] == nums[j-1]:
res.append(nums[i])
nums[i] = 0
nums[j-1], nums[i] = nums[i], nums[j-1]
return res
解法二:原地交换元素+判断i+1==nums[i]
针对解法一,其实可以优化成,while条件不再是原来条件,而是 nums[i]!=nums[nums[i]-1]。这样说明该元素重复,如果不满足条件,就原地交换即可,遍历完后,会将所有位置含有的元素(至少1个)交换到正确为止。最后判断i+1!=nums[i]的即是答案。
代码二: 原地交换元素+判断i+1==nums[i]
class Solution:
def findDuplicates(self, nums: List[int]) -> List[int]:
res = []
n = len(nums)
for i in range(n):
while nums[i] != nums[nums[i]-1]:
nums[nums[i]-1], nums[i] = nums[i], nums[nums[i]-1]
return [num for i, num in enumerate(nums) if num != i+1]
解法三:使用正负号作为标记
我们首先对数组进行一次遍历。当遍历到位置 i 时,我们考虑 nums[nums[i]−1] 的正负性:
- 如果 nums[nums[i]−1] 是正数,说明 nums[i] 还没有出现过,我们将 nums[nums[i]−1] 加上负号;
- 如果 nums[nums[i]−1] 是负数,说明 nums[i] 已经出现过一次,我们将 nums[i] 放入答案。
细节
- 由于 nums[i] 本身可能已经为负数,因此在将 nums[i] 作为下标或者放入答案时,需要取绝对值。
代码三: 使用正负号作为标记
class Solution:
def findDuplicates(self, nums: List[int]) -> List[int]:
res = []
for x in nums:
x = abs(x)
if nums[x-1] > 0:
nums[x-1] = -nums[x-1]
else:
res.append(x)
return res