原题
一个数组A,数字出现的情况,只有以下三种:
1. 一些数字只出现一次
2. 一些数字出现两次
3. 只有一个数字出现三次
请给出方法,找到出现三次的数字。
分析
这个题目和“找数字”的题目比较相似,但是解法上类似么?之前的解法是检查某一位上的1的和,是否能够被3整除,因为整数是32位的,可以开辟一个 32位大小的数组,这也是常数空间的。那么这个题目可以用这个方法来解决么?因为有不确定个数的数字出现了一次,这样可以产生的余数的种类也就比较多了。 那该怎么处理呢?
hashmap的方法被称为万金油,在牺牲了空间的条件下,很好的达到了O(n)的时间复杂度。
//hash: t.O(n);s.O(n)
bool isfound = false;
int findThreeTimeNum(int* arr, int length)
{
assert(NULL != arr && length>0);
if(length < 3)return -1;
unordered_map<int, int> mp;
unordered_map<int, int>::iterator it = mp.end();
for(int i = 0; i < length; i++)//O(n)
{
it = mp.find(arr[i]);
if(it == mp.end())
mp[arr[i]] = 1;
else
mp[arr[i]]++;
}
for(it = mp.begin(); it != mp.end(); it++)//O(n)
if(it->second == 3){
isfound = true;
return it->first;
}
return -1;
}
如果要求常数空间的解法呢?之前的文章也有讨论,快排的时间复杂度是O(nlogn),然后遍历一遍,找到连续三个相同的数字。后面这一遍遍历,可以省去,因为出现三次的数字只有一个。但总的时间复杂度仍是O(nlogn)。
//sort: O(nlogn)
bool isfound = false;
int findThreeTimeNum(vector<int> arr)
{
sort(arr.begin(),arr.end());
int times = 1;
int curnum = arr[0];
vector<int>::iterator it = arr.begin();
for(it++; it != arr.end(); it++)
{
if(curnum == *it)
times++;
else{
curnum = *it;
times=1;
}
if(times == 3)
{
if((it+1) == arr.end() || *(it+1) != curnum){
isfound = true;
return *it;
}
}
}
return -1;
}
是否还有其他的方法呢?有的同学给出了如下的方法:可以取得A中所有数字的乘积p,我们假设p没有溢出。这是遍历数组中的每一个元素A[i],查看 是否p % (A[i] * A[i] * A[i]) == 0,但此时,A[i]并不一定是最终要找到的数字,还需要遍历数组A,查看A[i]是否出现了三次。但这个方法整体的时间复杂度为O(n^2)。如果数组中的数都是素数的话,这个方法可行,复杂度为O(n).
这个题目,能够在时间复杂度O(n),空间复杂度O(1)的条件下完成么?