题目
给定一个整数数组 a,其中1 ≤ a[i] ≤ n (n为数组长度), 其中有些元素出现两次而其他元素出现一次。
找到所有出现两次的元素。
你可以不用到任何额外空间并在O(n)时间复杂度内解决这个问题吗?
示例:
输入:
[4,3,2,7,8,2,3,1]
输出:
[2,3]
想法
根据之前数组中的度的题目697题已经知道使用Map既可以存储数,也可以存储次数,所以就想到了使用Map来做。
很快啊,就做出来了;
class Solution {
public List<Integer> findDuplicates(int[] nums) {
List<Integer> list = new ArrayList<>();
Map<Integer,int[]> map = new HashMap<>();//int[2] 数字出现的次数
for(int i = 0 ; i < nums.length;i++)
{
if(map.containsKey(nums[i]))
{
map.get(nums[i])[0]++;
if(map.get(nums[i])[0] == 2 )
{
list.add(nums[i]);
}
}
else
{
map.put(nums[i],new int[]{1});
}
}
return list;
}
}
但是这个时间复杂度空间复杂度,还有提出 的要求都差了点。需要找到更好的方法。
其它想法
评论区使用的还是与448题相类似的方法,使用相反数或者是使用哈希表除余数+n那种方法,因此我就手写一下方法代码。
哈希表:
class Solution {
public List<Integer> findDuplicates(int[] nums) {
List<Integer> list = new ArrayList<>();
int n = nums.length;
for(int num:nums)
{
int x=(num - 1 ) % n;
nums[x] +=n ;
}
for(int i = 0 ; i <n ;i ++)
{
if(nums[i]>2*n)
{
list.add(i+1);
}
}
return list;
}
}
官方的哈希表就是快:
再来看看取反操作怎么样:
class Solution {
public List<Integer> findDuplicates(int[] nums) {
List<Integer> list = new ArrayList<>();
int n = nums.length;
for(int i = 0 ; i < n ; i ++)
{
int num =Math.abs(nums[i]);
if(nums[num-1]>0)
{
nums[num-1] *=-1;
}
else
{
list.add(num);
}
}
return list;
}
}
取反操作看之前的数下标所对应的数是否都是正数,正数就取反,如果为负数,说明已经取反过了,这样就是重复的数字。
总结
这两道题做的时候需要对提出的要求进行改进,不能再对数组做额外的空间了,我发现自己对取余反而用的较为熟悉了,但是 取反有点陌生,感觉理解了,好像还差了一点,多练练这个方法就好了,多加理解。