- 数组中重复的数据
给定一个整数数组 a,其中1 ≤ a[i] ≤ n (n为数组长度), 其中有些元素出现两次而其他元素出现一次。
找到所有出现两次的元素。
你可以不用到任何额外空间并在O(n)时间复杂度内解决这个问题吗?
示例:
输入:
[4,3,2,7,8,2,3,1]
输出:
[2,3]
1.利用hashMap实现计数的功能
class Solution {
public List<Integer> findDuplicates(int[] nums) {
int len = nums.length;
HashMap<Integer, Integer> map = new HashMap<>();
for(int num : nums){
map.put(num, map.getOrDefault(num, 0)+ 1);
}
List<Integer> list = new ArrayList<>();
for(int num: nums){
if(map.get(num) == 2 && !list.contains(num)){
list.add(num);
}
}
return list;
}
}
超时,首先,map遍历方式不对,这样超时是必然的
class Solution {
public List<Integer> findDuplicates(int[] nums) {
int len = nums.length;
HashMap<Integer, Integer> map = new HashMap<>();
for(int num : nums){
map.put(num, map.getOrDefault(num, 0)+ 1);
}
List<Integer> list = new ArrayList<>();
Set<Integer> set = map.keySet();//对set遍历即可
for(Integer num : set){
if(map.get(num) == 2){
list.add(num);
}
}
return list;
}
}
2.无穷交换方法
就是把所有的都归位,剩下没归位的就是重复的,要满足条件,数字在1-n之间
class Solution {
public List<Integer> findDuplicates(int[] nums) {
int len = nums.length;
List<Integer> list = new ArrayList<>();
if(len == 0) return list;
//仍是自造哈希表
for(int i = 0; i < len; i++){
while(nums[nums[i] - 1] != nums[i]){//通过该操作,将所有数字都归位
//注意对于那些重复的数字,在第一次碰到的时候,会被调整的对的位置,第二次就不会调整了
//此时nums[nums[i] - 1] == nums[i]
//交换,不占用空间,利用异或
swap(nums, i, nums[i] - 1);
}
}
//对于所有不在应有的位置,加入list
for(int i = 0; i < len; i++){
if(nums[i] - 1 != i){
list.add(nums[i]);
}
}
return list;
}
void swap(int[] num, int i, int j){
//注意特殊情况,i, j对应相同的值,特判
if(i== j) return;
num[i] = num[i] ^ num[j];
num[j] = num[i] ^ num[j];
num[i] = num[i] ^ num[j];
//交换也可使用下面的方法:
/*int tmp = nums[i];
nums[i] = nums[nums[i] - 1];
nums[tmp - 1] = tmp;//这里一定要使用tmp,中间有值变动
*/
}
}
3.抽屉法,利用负数标记法
遍历,每一个数-1作为index下标,对应位置*-1,同时记得最后要修改过来
找到数字i时,将位置i-1处的数字翻转为负数。
如果位置i-1 上的数字已经为负,则i是出现两次的数字。
关于负数标记法,常用的为L448,那种方法可以标记没出现的数字,因此这里要换一种方法:
class Solution {
public List<Integer>findDuplicates(int[] nums) {
int len = nums.length;
List<Integer> list = new ArrayList<>();
if(len == 0) return list;
for(int i= 0; i < len; i++){
//int index = Math.abs(nums[i] - 1);//别忘了取绝对值,这里写错了
int index = Math.abs(nums[i]) - 1;
if(nums[index] < 0){
list.add(index + 1);
//已经被标记了,说明当前的index已经出现过来,反推回来,index+1
}
nums[index] = -nums[index];//先标记
}
return list;
}
}