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]
一看到寻找重复,就先想到哈希表。O(n)时间复杂度很简单,只要遍历的时候放入哈希表,再出现的就是结果。但这样不满足空间复杂度的要求。一般来说时间和空间都是需要trade off的。保持时间不变减少空间,那么一定有其他的玄机在里面。这道题就是这样,看限定条件1 ≤ a[i] ≤ n ,就知道a[i]-1的范围是0~n,那么它其实可以用作数组的下标。也就是说,数组元素和数组下标其实是对应的关系(看起来像废话…)。每次访问一个元素,同时也是访问一个下标,那么再次访问这个元素的时候就会再次访问对应的下标。这样就可以在其对应的下标指向的元素上面做文章。有什么办法可以让元素既可以起到标记的作用,又不影响其可以继续做下标的属性呢?那就是取负数。这样如果将元素对应下标指向的元素取负数,就知道这个元素已经出现过。而取负数并不影响它可以做下标的属性,因为再取绝对值就好了。所以本质不是标记遍历到的元素本身,而是用负号标记遍历到的元素对应下标指向的元素。或者可以把本身看成一个哈希表,计算hash的过程同时也是标记的过程。
Java
class Solution { public List<Integer> findDuplicates(int[] nums) { List<Integer> res = new ArrayList<>(); if (nums == null || nums.length == 0) return res; for (int i = 0; i < nums.length; i++) { int index = Math.abs(nums[i]) - 1; if (nums[index] > 0) nums[index] *= -1; else { res.add(index + 1); //index + 1 == Math.abs(nums[i]) } } return res; } }