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]
题意:
给一个n个数的整数数组,数组元素大于1小于n,一部分元素出现一次,另一部分出现两次,找到数组中出现两次的元素。建议不使用另外的空间并且具有O(n)及以下的时间复杂度。
基本思路:
先不考虑最后一个建议要求,最直观的方法就是另外建一个从0到n的type型哈希表,表中所有元素的值为0.遍历数组,将元素的值对应哈希表中对应项自增,最后选取哈希表中值大于1的元素下标填入结果。
这个方法适用于所有求超过几次的问题,无论是两次,三次,五次。缺点就是不符合建议,申请了额外的n*sizeof(type)个字节的空间,时间复杂度仍然是O(n),但是要遍历两次。代码如下:
1 class Solution { 2 public: 3 vector<int> findDuplicates(vector<int>& nums) { 4 vector<int> new1; 5 vector<int> flag; 6 flag.resize(nums.size()+1); 7 for(auto elem:nums) 8 flag[elem]++; 9 for(size_t i=0; i<flag.size(); ++i) 10 if(1<flag[i]) 11 new1.push_back(i); 12 return new1; 13 } 14 };
所以为了完美符合题目要求,我们要想办法把flag空间和nums空间融合,并且一次遍历就能找到重复元素。
因为nums里面全是正数,所以可以用正负号来作为flag。遍历数组并访问元素的值对应下标的元素,如果被访问值是正数那就改为负数,当第二次访问到的时候,这个数必然为负数就把下标加入结果,一次遍历后直接返回结果。因为数组是从0开始,所以访问的时候要减一,加入结果时要加一。
代码如下:
1 class Solution { 2 public: 3 vector<int> findDuplicates(vector<int>& nums) { 4 vector<int> new1; 5 int len = nums.size(); 6 for(int i =0; i < len; ++i) 7 { 8 int index = abs(nums[i])-1; 9 if(nums[index]<0) 10 new1.push_back(index+1); 11 nums[index] = -nums[index]; 12 } 13 return new1; 14 } 15 };
新的问题:
如果题中有一个数出现了三次,找这个出现了三次的数怎么办?
思路:
按照简单粗暴的第一种思路肯定可以查出来,但是又不想增加那么多,怎么办?
现在我们在查的过程中已经有了一个标志位和结果数组,当这个数对应项访问到时如果已经为负数,就不再把它变为正数,并且使用find在结果矩阵中再找一次这个数是否已经在结果矩阵中,如果已经在那么就是出现了三次的数,如果不在就把这个数加入到候选结果但不能保证时间复杂的是O(n),根据选取的容器不同时间复杂度不同。当然这已经不是这道题的内容了。