本文记录力扣网上编程题目,主要涉及数组方面的,指针的使用来解决问题,这里以 C语言实现。
给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。
假设 nums 只有 一个重复的整数 ,返回 这个重复的数 。
你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。
示例 1:
输入:nums = [1,3,4,2,2]
输出:2
示例 2:
输入:nums = [3,1,3,4,2]
输出:3
示例 3 :
输入:nums = [3,3,3,3,3]
输出:3
提示:
1 <= n <= 105
nums.length == n + 1
1 <= nums[i] <= n
nums 中 只有一个整数 出现 两次或多次 ,其余整数均只出现 一次
进阶:
如何证明 nums 中至少存在一个重复的数字?
你可以设计一个线性级时间复杂度 O(n) 的解决方案吗?
一旦涉及出现次数,我们就可以考虑使用哈希表来解决问题。
1. 定义一个 numsSize大小的内存(这里动态分配一段内存,在最后使用结束后释放),输入数组元素作为该数组的下标;
2. 遍历输入的数组,统计数组元素的个数;
3. 再遍历一次数组(这里数组为动态分配的内存),找到个数 大于等于2时,返回原数组元素;
C语言实现如下:
//类似哈希表的方法
int findDuplicate(int* nums, int numsSize) {
if((nums == NULL) || (numsSize <= 0)) {
return 0;
}
int i;
int result = 0;
int* ptr = (int*)malloc(numsSize*sizeof(int));
memset(ptr, 0, numsSize*sizeof(int));
for(i = 0; i < numsSize; i++) {
ptr[nums[i]]++;
if(ptr[nums[i]] > 1){
result = nums[i];
break;
}
}
free(ptr);
ptr = NULL;
return result;
}
可以看出,上面的实现方法的时间复杂度为O(n),空间复杂度为O(n),空间复杂度不满足题目要求。
二分查找,又叫折半查找。是一种在有序数组中查找特定元素的高效算法。
其核心思想是通过不断将查找范围缩小一半,快速定位目标元素的位置。时间复杂度为 O (log n),相比 O (n) 的线性查找效率显著提升。
该题目关键点:
(1) 数组长度为 n+1
,元素范围是 1
到 n
。
(2) 只有一个重复数,但可能重复多次。
题目中的数值范围 [1, n]
是有序的,我们可以利用这个范围进行二分查找。所以,该题目可以使用二分法的核心在于:它不是对数组本身的元素进行二分,而是对可能存在的数值范围进行二分。
下面举例说明:
1. 先确定答案的可能范围 [left, right]
。
2. 每次猜一个中间值 mid
,通过统计信息判断答案在左半部分还是右半部分。
3. 不断缩小区间,直到找到答案。
1. 定义一个左右边界值:left =1,right = numsSize-1;定义一个二分值,进行二分:mid = left+ (right-left)/2;
2. 对数组进行遍历,统计数组中 < mid的元素个数 count;
3. 比较 count 与 mid值大小:
count > mid:重复数在区间的左半边,[1, mid];则 right = mid;
count <= mid:重复数在区间的右半边,[mid+1, n];则 left = mid+1;继续进行以上的计算查找,直到 left = right,则目标值就是 left(或right)。
C语言实现如下:
//二分法
int findDuplicate(int* nums, int numsSize) {
if((nums == NULL) || (numsSize <= 0)){
return -1;
}
int left = 1;
int right = numsSize-1;
int count = 0;
int mid = 0;
while(left < right) {
count = 0;
mid = left+(right-left)/2;
//遍历数组,统计数组中小于 mid的元素个数
for(int i = 0; i < numsSize; i++){
if(nums[i] <= mid) {
count++;
}
}
//比较 count值与 mid值
if(count > mid) { //重复数在[1, n]的左半边:[1,mid]
right = mid;
}
else { //重复数在[1,n]的右半边:[mid+1, n]
left = mid+1;
}
}
//直到left == right时,即可找到重复的数
return left;
}
可以看出,二分查找每次将搜索范围缩小一半,因此循环次数为 O(log n),每次二分查找需要遍历整个数组,统计小于中间值 mid
的元素个数,时间复杂度为 O(n),所以,总的时间复杂度为O(n) × O(log n) = O(n log n)。空间复杂度为 O(1),满足题目要求。