刷题笔记:力扣第287题-寻找重复数

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)。

相关推荐
SHARK_pssm2 分钟前
【C++——类和对象(下)】
开发语言·c++·经验分享·笔记
Ivanqhz32 分钟前
层归一化、残差、前馈网络与激活函数简述
服务器·数据库·人工智能·深度学习·算法
不会就选b1 小时前
算法日常・每日刷题--<动态规划>3
数据结构·算法
Ztt6666666662 小时前
壶口敞开以后,器身反而更显从容
笔记·其他·百度·微信公众平台·微信开放平台
-dzk-2 小时前
【堆】LC 347.前 K 个高频元素
算法·堆
H.莓飛2 小时前
【数据结构】栈_OJ题
linux·数据结构·算法·centos
Zootopia6262 小时前
多架 eVTOL集群排班调度方案思考
人工智能·算法·数学建模·matlab·动态规划·无人机·evtol
和裕3 小时前
年度框架直供 vs 零散按需采购:定制纸箱采购成本、交付与服务核心区别全对比
大数据·运维·网络·人工智能·算法
朝朝辞暮i3 小时前
C++ 第 13 课:值传递 —— 为什么函数里改了,外面却没变?
java·c++·算法
淡海水3 小时前
02-02-原理篇-分代GC
算法·unity·c#·游戏引擎·.net·gc