剑指offer 3 . 数组中重复的(所有)数字 p39
题目:在一个长度为n的数组里的所有数字都在0到n-1的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。例如,如果输入长度为7的数组{2, 3, 1, 0, 2, 5, 3},那么对应的输出是重复的数字2或者3。
1. 允许修改数组
因为每个数字都只能对应一个索引,从索引i=0开始一步一步找到所有i对应的数,那么一定会慢慢逼近到有个值是i,但是不在索引i(i已经有一个值=i了)上。
尽管有for,while两重循环,但是基本上每一次比较之后要么前进,要么交换(交换后保证一个元素处在自己的索引位置, 遍历到它的时候就跳过,这也相当于前进了),所以时间复杂度还是O(n),空间复杂度是O(1)
void duplication (int numbers[], int length, set<int > &ans) {
if (length < 2 || numbers == NULL) return ;
for (int i = 0; i < length; ++i) // 数组中出现值超过索引界限
if (numbers[i] >= length || numbers[i] < 0) return;
for (int i = 0; i < length; ++i) {
while (numbers[i] != i) { // 一定会满足
if (numbers[i] == numbers[numbers[i]]) {
ans.insert(numbers[i]);
break;
} else {
int temp = numbers[i];
numbers[i] = numbers[temp];
numbers[temp] = temp;
}
}
}
}
int main() {
int a[] = {1,2,3,4,1,1,2,3,3,3};
set<int> ans; // 把所有数字存入,用set会过滤掉重复数字
duplication(a, sizeof(a) / sizeof(a[0]), ans);
if (ans.size() == 0) cout << 0 <<endl; // 不存在
set <int> :: iterator it;
for (it = ans.begin(); it != ans.end(); ++it)
cout << *it << endl;
return 0;
}
2. 不允许修改数组 P41
找出数组中重复的数字用O(1)空间,nlogn时间,不能找出来所有重复数字
int countNumbersBetweenStartAndEnd(const int *nums, int length,int start,int end) {
if (nums == NULL || length < 1) return 0;
int count = 0;
for (int i = 0; i < length; ++i) {
if (nums[i] >= start && nums[i] <= end) ++count;
}
return count;
}
int findRepeatNumber(const int *nums, int length) {
if (nums == NULL || length < 2) return -1;
int start = 1; //这是边界范围,不是索引
int end = length - 1;
while (start <= end) {
int mid = ((end - start) >> 1) + start;
// 默认先在左边范围统计
int count = countNumbersBetweenStartAndEnd(nums, length, start, mid);
if (start == end) { // 循环出口
if (count >= 2) return start;
else return -1; // 没有找到
} else {
if (count > mid - start + 1) end = mid; // 确实在左边重复
else start = mid + 1; // 左边没有重复右“循环”
}
}
}