Given an array of integers, 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and others appear once.
Find all the elements that appear twice in this array.
Could you do it without extra space and in O(n) runtime?
Example:
Input:
[4,3,2,7,8,2,3,1]
Output:
[2,3]
题意很好理解,就是在整型数组中找出重复的数,还没有算法思维的我,首先想到的就是暴力循环
public List<Integer> findDuplicates(int[] nums) {
List<Integer> resoult = new ArrayList<>();
for (int i=0;i<nums.length;i++){
for (int j=i+1;j<nums.length;j++){
if (nums[i] == nums[j]){
resoult.add(nums[i]);
break;
}
}
}
return resoult;
}
很显然, LeetCode直接报了Time Limit Exceeded。时间上不允许,我们必须在遍历第一遍的同时就把重复的数给找出来。
如何才能做到一次遍历就能找出重复的数呢?用我们人类的思维去考虑,当我们在读这个数组的时候,读到重复的数很自然就知道前面有没有出现,我们也是就读了一遍;这是因为我们读过这个数之后都会在大脑里面做个标记,等到再次读到这个数是就很很清楚这个数已经出现过了。
同理,那么我们让计算机也拥有同种思维,题中有一个很关键的条件1 ≤ a[i] ≤ n,根据这个条件我们应该有思路了,我们用类似哈希表作用的数组来实现,计算机每遍历一个数,就用这个数当下标在哈希数组里面做个标记,等到再次读到这个数的时候只需要进行判断就可以了。
上代码
public List<Integer> findDuplicates(int[] nums) {
int[] hashMap = new int[nums.length+1];
List<Integer> resoult = new ArrayList<>();
for (int num : nums ){
if (hashMap[num] == 1 ){
resoult.add(num);
}else {
hashMap[num] = 1;
}
}
return resoult ;
}
这种思路还可以衍生出以下解体方法,我们直接在数组本身上面做标记,因为1 ≤ a[i] ≤ n,所以数组里面的值都是正数,所以计算机每遍历一个数,我们就把这个数当作下标,然后在对应下标位置的数乘以-1,标记一下,这样再遍历到重复的数的时候,只需要判断正负,计算机就知道这个数是否已经出现过
public List<Integer> findDuplicates(int[] nums) {
List<Integer> resoult = new ArrayList<>();
for (int num : nums){
int index = Math.abs(num)-1;
if (nums[index] < 0){
resoult.add(index+1);
}else {
nums[index] *= -1;
}
}
return resoult;
}
在碰到 1 ≤ a[i] ≤ n 这种条件时,应该要往这中思路上考虑一下。