剑指 Offer 03. 数组中重复的数字(C)

简介: 在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。


⭐️题目来源


在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。


示例 1:

输入:

[2, 3, 1, 0, 2, 5, 3]

输出:

2 或 3


限制:

2 <= n <= 100000


方法一:直接遍历(超出时间限制)



思路:将数组中的每一个数字和后面的所有数字进行比较,当发现相等时,即为结果。

int findRepeatNumber(int* nums, int numsSize){
    int i,j;
    for(i=0;i<numsSize;i++)
    {
        for(j=i+1;j<numsSize;j++)
        {
            if(nums[i]==nums[j])
            return nums[i];
        }
    }
    return -1;
}

85505299e2d74ac89f3408a69438b144.png


方法二:排序 + 遍历(执行用时:32 ms 内存消耗:10.5 MB)


思路:排序后能保证重复的数字连续分布,这样遍历时只需要比较当前数字和下一个数字是否相同即可。


这里需要用到C语言本身的库函数qsort–>如果不清楚的小伙伴可以看看这篇博客

int cmp(const void* a,const void* b)
{
    return (*(int*)a-*(int*)b);
}
int findRepeatNumber(int* nums, int numsSize){
    int i;
    qsort(nums,numsSize,sizeof(int),cmp);
    for(i=0;i<numsSize-1;i++)
    {
        if(nums[i]==nums[i+1])
        return nums[i];
    }
    return -1;
}

702c932f1d41439ba9f0a6572e9b1f13.png


方法三:哈希查找(执行用时:28 ms 内存消耗:10.4 MB)



思路:创建哈希数组(key:数字,value:出现次数),遍历时先检查当前索引对应的数字是否已经出现,出现则返回结果,否则更新该数字出现次数。

#define NUMS_SIZE  100000
int findRepeatNumber(int* nums, int numsSize){
    int numsHash[NUMS_SIZE]={0};
    for(int i=0;i<numsSize;i++)
    {
        if(numsHash[nums[i]]>0)
        {return nums[i];}
        numsHash[nums[i]]++;
    }
    return -1;
}

653dfd96f53a440fa6875e02be6bfd02.png


方法四:原地排序(执行用时:24 ms 内存消耗:10.3 MB)



思路:借用输入数组,将当前索引对应的数字,替换到对应的数字索引下,保证索引和数字相等。如果出现替换时,索引和数字已经相等时,说明该数字重复,返回结果。相比于思路三的优点是不需要申请额外的空间存放Hash表。执行过程如下图。

d487c638c9584945adcfa1e16a60cf7f.pngaf44ed697cdb4c34adeb0eb7a44056e5.png8531496fed324cfdb34ae799c8d1e51b.png

int findRepeatNumber(int* nums, int numsSize){
    int temp=0,cur=0;
    while(cur<numsSize)
    {
        if(nums[nums[cur]]!=nums[cur])
        {
            temp=nums[cur];
            nums[cur]=nums[temp];
            nums[temp]=temp;
            continue;
        }
        if(cur==nums[cur])
        {
            cur++;continue;
        }
        return nums[cur];
    }
    return -1;
}


方法五:二分法(解答错误)



根据题目发现答案在[0, n-1]中, left = 0, right = n - 1, mid = (left + right) / 2,先计算整个数组中[left, mid]范围内的数的数量,如果大于


(mid - left),说明左侧有重复数字,right = mid,否则,left = mid,继续基于新的left, right二分。该种思路无法解决场景[0, 1, 2, 0, 4, 5, 6, 7, 8, 9]。

int Count(int* nums, int numsSize, int a, int b)
{
    int cnt = 0;
    int cur = 0;
    while (cur < numsSize) {
        if (nums[cur] >= a && nums[cur] <= b) {
            cnt++;
        }
        cur++;
    }
    return cnt;
}
int findRepeatNumber(int* nums, int numsSize){
    // 二分法 无法解决场景:[0, 1, 2, 0, 4, 5, 6, 7, 8, 9]
    int left = 0;
    int right = numsSize - 1;
    int mid, count;
    while (left < right - 1) {
        mid = (left + right) / 2;
        count = Count(nums, numsSize, left, mid);
        if (count > mid + 1 - left) {
            right = mid;
            continue;
        }
        left = mid;
    }
    if (Count(nums, numsSize, left, left) > 1) {
        return left;
    }
    return right;
}

097c2727bbfd4636a92fd4d09aa4f216.png


总结



感觉刷题的话还是要认真把每一题搞懂才行,看着力扣的大佬们都有很多中解法,也学习到了许多,我自己昨天才刚刚学到qsort函数,今天就用到了,加油吧!



相关文章
|
4月前
剑指 Offer 03:数组中重复的数字
剑指 Offer 03:数组中重复的数字
13 0
|
4月前
剑指 Offer 56 - I:数组中数字出现的次数
剑指 Offer 56 - I:数组中数字出现的次数
19 0
|
4月前
剑指 Offer 56 - II:数组中数字出现的次数 II
剑指 Offer 56 - II:数组中数字出现的次数 II
22 0
|
4月前
剑指 Offer 53 - I:在排序数组中查找数字 I
剑指 Offer 53 - I:在排序数组中查找数字 I
23 0
|
4月前
剑指 Offer 50:第一个只出现一次的字符
剑指 Offer 50:第一个只出现一次的字符
20 0
|
4月前
剑指 Offer 20:表示数值的字符串
剑指 Offer 20:表示数值的字符串
26 0
|
4月前
剑指 Offer 43:1~n 整数中 1 出现的次数
剑指 Offer 43:1~n 整数中 1 出现的次数
31 0