题目:在一个长度为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; // 左边没有重复右“循环” } } }