LeetCode #442 Find All Duplicates in an Array 数组
Description
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]
思路
老年人回炉重造第5天。
这道题比较tricky,要求时间复杂度 O(n),no extra space(题目补充说明了待返回的数组不算在 extra space 里,所以可以开辟一个数组保存算法的结果),没想出来,看了油管的一个讲解视频有了思路后才敲出来,视频链接如下:
《LeetCode 442. Find All Duplicates in an Array (Solution Explained)》
本题的难点之一是如何正确理解题目,我一开始眼睛瞟了,只看见一个关键信息“所有数字在输入串中只能出现一次”,而没有看到关键信息“1 ≤ a[i] ≤ n (n = size of array)”。该信息的意思是:数组内所有元素的值都在 1 与 size 之间。
本来必须有两层循环才能解题,但是根据这个关键信息,我们可以让内层循环变成“映射”,从而使时间复杂度降为 O(n)。内层“映射 Key-Value”的方法是:把数字减一的值作为 index(Key),将 index 对应位置的值改为负号以便标记一个数字已经访问过了(Value)。
能这么做的基础有两个:
1. 减一之后,数组内元素值的范围在 0 与 size -1 之间,刚好与 index 相等。
2. 数组中最多只有两个相同的数字,更好可以使用正负号进行唯一标记。如果允许数组中最多有三个相同数字的话,就得换一种 Hash 策略了。
AC后,发现一个小技巧,让一个可能为负的值变成正值,除了使用 if (x < 0) x = -x 或 if (x < 0) x *= -1 这两个办法之外,还可以使用 x = abs(x),它的效率好像会比“条件判断再赋值”的流程更高(复议)
AC代码如下,114ms,74% ranking:
class Solution { public: vector<int> findDuplicates(vector<int>& nums) { vector<int> result; // use set if appear more than twice if (nums.size() < 2) return result; for (auto element : nums) { // element may be negative, don't use it directly as index //if (element < 0) element = -element; element = abs(element); if (nums[element-1] < 0) { result.push_back(element); } else { nums[element-1] = -nums[element-1]; } } return result; } };