

1.本题最简单的做法便是利用哈希表,统计数组中所有元素出现的个数,超过1次的元素即为答案。完整代码如下:
cpp
1. // 寻找数组中重复的数字
2. int findDuplicate(int* nums, int numsSize) {
3. // 定义哈希数组,下标对应数字,值记录出现次数,初始化为0
4. int hash[100001] = {0};
5. // 遍历数组每一个元素
6. for (int i = 0; i < numsSize; i++){
7. // 当前数字对应哈希位置计数+1
8. hash[nums[i]]++;
9. // 如果计数大于1,说明是重复数字,直接返回
10. if (hash[nums[i]] > 1) return nums[i];
11. }
12. // 没有找到重复数字返回-1
13. return -1;
14. }
该算法时间复杂度为O(n),空间复杂度为O(1)。虽然空间复杂度为常数级别,但实际上也开辟了一块大小为sizeof(int) * 100001的空间,不符合题中只用常量级空间的要求。
2.第二个想到的方法是将数组进行排序,然后进行遍历,当前元素等于上一个元素的时候答案即为该元素。完整代码如下:
cpp
1. // qsort排序的比较函数,升序排序
2. int cmp(const void* a, const void* b){
3. // 将void指针转为int指针,解引用后返回差值
4. return *(int*)a - *(int*)b;
5. }
6. // 寻找数组中重复的数字
7. int findDuplicate(int* nums, int numsSize) {
8. // 使用qsort对nums数组进行升序排序
9. qsort(nums, numsSize, sizeof(int), cmp);
10. // 遍历排序后的数组
11. for (int i = 0; i < numsSize; i++){
12. // 下标大于0,且当前元素与前一个元素相等,找到重复值
13. if (i > 0 && nums[i] == nums[i - 1]){
14. // 返回重复数字
15. return nums[i];
16. }
17. }
18. // 不存在重复数字返回-1
19. return -1;
20. }
该算法时间复杂度为O(nlogn),空间复杂度为O(1)。
3.也可以对1, n进行二分查找,每次遍历数组统计比mid小的元素个数,如果大于mid就说明在左边,反之在右边。完整代码如下:
cpp
1. // 寻找数组重复数字,二分查找思路
2. int findDuplicate(int* nums, int numsSize) {
3. // 初始化二分左右边界
4. int l = 0, r = numsSize - 1;
5. // 二分循环条件:左边界小于等于右边界
6. while (l <= r){
7. // 计算中间值mid
8. int mid = (l + r) / 2;
9. // cnt记录数组中<=mid的数字数量
10. int cnt = 0;
11. // 遍历数组统计个数
12. for (int i = 0; i < numsSize; i++){
13. // 判断当前元素是否小于等于mid
14. if (nums[i] <= mid){
15. // 计数+1
16. cnt++;
17. }
18. }
19. // 小于等于mid的数超过mid个,重复数落在左半区间
20. if (cnt > mid){
21. // 更新右边界
22. r = mid - 1;
23. } else {
24. // 重复数落在右半区间,更新左边界
25. l = mid + 1;
26. }
27. }
28. // 循环结束l就是重复的数字
29. return l;
30. }
该算法时间复杂度为O(nlogn),空间复杂度为O(1)。
4.在力扣第142题-环形链表II中涉及到了一个经典的快慢指针Floyd法,当确定一个数据结构内部有一个环的时候可以用这个方法来寻找这个环的起点。本题可以将每个元素当成下标,每次都去寻找数组中这个下标指向的值,而对于重复元素target,因为它占据了两个位置,所以一定有两个指向它的下标,即一定有两条边指向它,故一定能形成一个闭环,而target就是这个环的入口。Floyd法的具体解析在力扣第142题-环形链表II中已经进行推理,这里直接给出完整代码:
cpp
1. // 寻找数组中重复数字,快慢指针(弗洛伊德判圈算法)
2. int findDuplicate(int* nums, int numsSize) {
3. // 定义快慢指针,初始都指向0位置
4. int fast = 0, slow = 0;
5. // 第一轮:快指针走两步,慢指针走一步,直到两者相遇
6. do {
7. // 快指针一次移动两步
8. fast = nums[nums[fast]];
9. // 慢指针一次移动一步
10. slow = nums[slow];
11. } while (fast != slow);
12. // 将慢指针重置回起点
13. slow = 0;
14. // 第二轮:快慢指针均每次走一步,相遇点即为环入口(重复数字)
15. while (fast != slow){
16. // 快指针每次移动一步
17. fast = nums[fast];
18. // 慢指针每次移动一步
19. slow = nums[slow];
20. }
21. // 返回相遇的值,就是重复数字
22. return fast;
23. }
该算法时间复杂度为O(n),空间复杂度为O(1)。