问题描述:
给出一个长度为n的数组,数组里的每个元素取值介于1-n之间,有的元素可能会多次出现,有的元素可能不会出现。
现在要把没有在1-n里没有在数组中出现的元素找出来。
要求do it without extra space and in O(n) runtime。
解决:
如果没有空间复杂度的要求,借助于map存储每个不同的元素,这些元素又是1-n范围内的,很容易求出属于1-n但不在map内的元素。
但是题目有了不使用额外空间的要求,问题就没那么容易了。比较好的一种做法是利用数组的下标,因为1<=nums[i]<=n,0<=i<=n-1,
如果数组中存在元素k=nums[i],那么将坐标为k-1上的元素值取相反数,作为一种标记。即nums[Math.abs(nums[i])-1] = -nums[Math.abs(nums[i])-1]
使用Math.abs是为了解决nums[i]可能已经为负的问题。当元素k重复出现时可能会出现偶数次取反负负得正的情况,所以只对为正的
元素进行取反(也就是相同元素第一次出现时取相反数)。
经过一次遍历后,出现的元素其对应的值减1的坐标上的值是负数,而没有出现的元素其对应的值减1的坐标上的值是正数。
然后将所有正的元素的坐标值+1添加到list中。说起来比较绕,举个例子:
[4,3,2,7,8,2,3,1] 经过一次遍历操作之后为[-4,-3.,-2,-7,8,2,-3,-1],其中8,2两元素值为正,它们的坐标分别是4,5。那么原数组中缺少的数字
就是5和6。
/*
* Given an array of integers where 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and others appear once.
Find all the elements of [1, n] inclusive that do not appear in this array.
Could you do it without extra space and in O(n) runtime? You may assume the returned list does not count as extra space.
Example:
Input:
[4,3,2,7,8,2,3,1]
Output:
[5,6]
*/
public class FindAllNumbersDisappearedInAnArray {
public List<Integer> findDisappearedNumbers(int[] nums) {
List<Integer> list = new ArrayList<Integer>();
for(int i=0; i<nums.length; i++) {
int val = Math.abs(nums[i]) - 1;
if(nums[val] > 0) {
nums[val] = -nums[val];
}
}
for(int i=0; i<nums.length; i++) {
if(nums[i]>0) {
list.add(i+1);
}
}
return list;
}
}