力扣网C语言编程题:“寻找重复数”的两种思路

一. 简介

本文记录力扣网上编程题目,主要涉及数组方面的,指针的使用来解决问题,这里以 C语言实现。

二. 力扣网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,元素范围是 1n

(2) 只有一个重复数,但可能重复多次。

题目中的数值范围 [1, n] 是有序的,我们可以利用这个范围进行二分查找。所以,该题目可以使用二分法的核心在于:它不是对数组本身的元素进行二分,而是对可能存在的数值范围进行二分。

下面举例说明:

力扣网C语言编程题:“寻找重复数”的两种思路_第1张图片

二分法的方法:

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),满足题目要求。

你可能感兴趣的:(逻辑编程题,C语言,leetcode,算法)