题目
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
1.思路
思路1
(1)新建一个数组index,遍历原数组numbers,用numbers[i]作为下标访问数组index,并将index[ numbers[i] ] ++;
(2)遍历数组index,如果遇到index[i] >= 2,说明有重复数字,返回true,否则遍历结束之后返回false即可。
思路2
新建一个set,遍历数组并将其元素依次存入set,借助其计数函数count()来判断数组中是否有元素重复,因为set是无重复值的一种数据结构,所以只要当前访问到的原数组中的元素值在set中计数不为0,说明是重复元素,直接将其返回即可。
思路3
新建一个map<key,value>,其中,以原数组中的函数值作为key,布尔值bool作为value,即map初始化为map<int,bool>,map的特点是key值无重复,初次遍历到的数组元素,将其对应在map中的value值设置为true,当在数组中再次遍历到相同元素时,便会发现其value值已经为true,直接返回该重复元素即可。该思路与思路2很相似。
思路4
排序后遍历,存在相等的相邻元素,即为重复。
思路5
这是一个不成熟的思路,运行时间不符合标准,但是很容易被想到,双重循环遍历查找重复元素。
思路6
引用 剑指offer 作者的思路
在一个长度为n的数组nums里的所有数字都在0 ~ n-1的范围内。即数组元素的索引和值是一对多的关系。因此,可遍历数组并通过交换操作,使元素的索引与值一一对应(即nums[i]=i)。
遍历过程中,第一次遇到数字x时,将其交换至索引x处;而当第二次遇到数字x时,一定有nums[x]=x,此时即可得到一组重复数字。
算法流程:
遍历数组 nums ,设索引初始值为 i = 0 :
若nums[i] = i:说明此数字已在对应索引位置,无需交换,continue;
若nums[nums[i]] = nums[i]:代表索引nums[i]处和索引i处的元素值都为nums[i],即找到一组重复值,返回nums[i];
否则:交换索引为i和nums[i]的元素值,将此数字交换至对应索引位置。
若遍历完毕尚未返回,则返回 -1。
复杂度分析:
时间复杂度O(N):遍历数组使用O(N),每轮遍历的判断和交换操作使用O(1) 。
空间复杂度O(1):使用常数复杂度的额外空间。
2.代码
思路1 Java实现 借助数组
public class Solution {
public boolean duplicate(int numbers[],int length,int [] duplication) {
int [] index = new int[length+1]; //index的容量多设些
int i = 0;
//遍历numbers数组并更新index数组
for(i = 0; i < length; i++){
index[numbers[i]] ++;
}
//判断有没有重复数字
for(i = 0; i < length; i++){
if(index[i] >= 2){
duplication[0] = i;
return true;
}
}
return false;
}
} //O(n)
思路2 c++实现 借助set
class Solution
{
public:
int findRepeatNumber(vector<int>& nums)
{
set <int> temp;
for (int i = 0; i < nums.size(); i++){
cout<<nums[i]<<endl;
if (temp.count(nums[i]) != 0){
return nums[i];
}
temp.insert(nums[i]);
}
return -1;
}
};
思路3 c++实现 借助map
class Solution
{
public:
int findRepeatNumber(vector<int>& nums)
{
map<int, bool> temp;
for (std::vector<int>::iterator it = nums.begin(); it != nums.end(); ++it)
{
if (temp[*it])
{
return *it;
}
temp[*it] = true;
}
return -1;
}
};
思路4 c++实现 排序后遍历
class Solution
{
public:
int findRepeatNumber(vector<int>& nums)
{
sort(nums.begin(), nums.end());
for (int i = 0; i < nums.size() - 1; i ++){
if (nums[i] == nums[i+1]){
return nums[i];
}
}
return -1;
}
};
思路5 c++实现 双重遍历 时间复杂度不符合要求
class Solution
{
public:
int findRepeatNumber(vector<int>& nums)
{
std::vector<int>::iterator it, itr;
for (it = nums.begin(); it != nums.end(); ++it){
for (itr = it+1; itr != nums.end(); ++itr){
//printf("%d, %d\n", *it, *itr);
if (*it == *itr){
return *it;
}
}
}
return -1;
}
};
思路6 c++实现 剑指offer作者的巧妙思路
class Solution
{
public:
int findRepeatNumber(vector<int>& nums)
{
int i = 0;
while(i < nums.size())
{
if(nums[i] == i)
{
i++;
continue;
}
if(nums[nums[i]] == nums[i])
{
return nums[i];
}
int temp = nums[i];
nums[i] = nums[nums[i]];
nums[temp] = temp; // 注意这里不能写成nums[nums[i]]!因为nums[i]的值已经改变了!
}
return -1;
}
};