题目描述
在一个数组长度为n的数组里,所有数字都在 0 ~ n-1 范围内,数组中某些数字是重复的,但不知道几个数字重复了,也不知道每个数字重复了多少次,请找出数组中重复的数字。
思路一:插排思想(比较挫,时间复杂度:n²)
因为要输出任意一个重复的数字,只要一个数字出现次数等于两次,就可以输出这个数了。可以选取第一个数作为基准,依次遍历这个数之后的所有数,如果有相等的数,就直接返回;如果没有,就选取第二个数作为基准,依次遍历第二个数后面的所有数,如果有相等的,就返回。……
思路二:哈希思想(空间换取时间)
开辟一个大小为n的数组HashTable,遍历一遍用户输入的数组,将数组中的元素映射到HashTable数组中,比如:用户数组中有5,7,5,就分别将HashTable中下标为5的位置+1,下标为7的位置+1,下标为5的位置再+1,直到遍历完成。那么每个数的出现次数都有了。
- 代码写的比较挫,没有按题意写,但是思路是对的。
#include<iostream>
#include<cstdio>
using namespace std;
void Hash_Search(int *arr, int sz)
{
int HashTable[1024] = { 0 };
for (int i = 0; i < sz; ++i)
{
cout << "存放" << arr[i] << endl;
HashTable[(arr[i])] ++;
}
cout << endl;
for (int j = 0; j < sz; j++)
{
cout << "查看" << j << "出现次数: " << HashTable[j] << endl;
}
cout << endl;
for (int j = 0; j < sz; j++)
{
if (HashTable[j] >= 2)
{
//return arr[j];
cout << "结果:" << arr[j - 1] << endl;
exit(0);
}
}
}
int main()
{
int arr[6] = { 1, 4, 3, 3, 2, 3 };
Hash_Search(arr, 6);
return 0;
}
这种方法同样可以解决:数组中出现次数超过一半的数,数组中出现了N次的数,字符串中出现N次的字符,员工统计水果问题。
思路三:修改原数组顺序解法(剑指Offer解法)
首先给定数组arr[] = {2, 3, 0, 1, 3}。因为数组中最大的数是n-1,那就一个萝卜一颗坑。从0号下标位置开始,0号元素为2,不等于0,交换0号和2号位置,数组变为:
{0, 3, 2, 1, 3}。再比较0号位置,下标和值相等,往后走到1号下标元素为3,不等于1,交换1号和3号,变成:{0, 1, 2, 3, 3}。再继续,下标来到4号位置,发现元素下标与值不相等,比较4号和3号下标位置,发现元素相等,返回3即可。
#include<iostream>
#include<cstdio>
using namespace std;
int Hash_Search(int *arr, int sz)
{
if (arr == NULL || sz <= 0)
return -1;
for (int i = 0; i < sz; i++)
{
if (arr[i] < 0 || arr[i] > sz - 1)
return -1;
}
for (int i = 0; i < sz; i++)
{
while (arr[i] != 1)
{
if (arr[i] == arr[arr[i]])
{
//cout << arr[i] << endl;
//exit(0);
return arr[i];
}
//交换
swap(arr[i], arr[arr[i]]);
}
}
return -1;
}
int main()
{
int arr[6] = { 1, 4, 0, 2, 2, 3 };
int GetNum = Hash_Search(arr, 6);
cout <<"输出:" <<GetNum << endl;
return 0;
}
思路四:不修改原数组顺序解法(剑指Offer解法:分割统计次数)
不高明解法:
不修改原数组可以创建一个复制空间,大小为n,在辅助空间上按思路三求解也可以,但是需要O(n)的辅助空间。
高明解法:
首先将数组一分为二,假设数组有8个元素。那么元素大小都在 0 ~ 7 之间。一分为二,如果没有重复,四个元素在 0 ~ 3 之间,四个在 4 ~ 7 之间。假设数组为arr[8] = {1, 0, 2, 3, 3, 4, 5, 6},统计0 ~ 3 之间的元素有5个, 统计 4 ~ 7 之间的元素有3个。说明,在 0 ~ 3 之间肯定有重复元素。接下来划分 0 ~ 3的5个元素{1, 0, 2, 3, 3}。 0 ~ 1 之间有两个元素, 2~3之间有三个元素。再划分2 ~ 3之间的元素,2有一个,3有两个,所以重复的是元素3。
和不高明解法相比,这是以时间换区空间。
#include <iostream>
#include <vector>
using namespace std;
int countrange(vector<int> vec, int begin, int end, int length)
{
int count = 0;
for (int i = 0; i<length; ++i)
{
if (vec[i] >= begin && vec[i] <= end)
++count;
}
return count;
}
int duplication(vector<int> vec)
{
// 条件检测
int length = vec.size();
if (vec.size() == 0)
return -1;
for (int i = 0; i<length; ++i)
{
if (vec[i]<1 || vec[i]>length - 1)
return -1;
}
int begin = 1;
int end = length - 1;
while (begin <= end)
{
int mid = ((end - begin) >> 1) + begin;
int count = countrange(vec, begin, mid, length);
if (end > begin)
{
// 更新数字范围
if (count>(mid - begin + 1))
end = mid;
else
begin = mid + 1;
}
else
{
if (count > 1)
return begin;
else
break;
}
}
return -1;
}
int main()
{
vector<int> vec = { 2, 2, 3, 3, 4, 5, 6 };
int num = duplication(vec);
cout << "结果:" << num << endl;
return 0;
}