(三)数组中的重复数字

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

这里的方式其实不局限于一两种,可以先排序然后再依次的进行比较。书中提供了一种比较简单的方式。

注意这里不是要找出所有重复的数字,只要找到一个就行了。

 

由于数组中的数字都是在0~n-1范围内的,如果数组中没有重复的数字,那么数组排序以后的数字 i 将出现在data[i]的位置。

一边在重排数组,一边也在查找重复的数字,这样的效率会比较高。

直接上代码:

#include "../common.h"

bool findDuplicateNumbers(int *dataNumbers, int length, int *duplicate)
{
    if(dataNumbers == nullptr)
    {
        return false;
    }
    for(int i = 0; i < length; ++i)
    {
        if(dataNumbers[i] < 0 || dataNumbers[i] > length - 1)
        {
            return false;
        }
    }
    for(int i = 0; i < length; ++i)
    {
        while(dataNumbers[i] != i)
        {
            if(dataNumbers[i] == dataNumbers[i])
            {
                *duplicate = dataNumbers[i];
                return true;
            }

            int temp = dataNumbers[i];
            dataNumbers[i] = dataNumbers[temp];
            dataNumbers[temp] = temp;
        }
    }
    return false;
}

int main()
{
    int data[7] = {2,3,1,0,2,5,3};
    int duplicate = -1;
    if(findDuplicateNumbers(data,7,&duplicate) != false)
    {
        printf("duplicate:%d\n",duplicate);
    }
    else
    {
        printf("No duplicate data !!\n");
    }  
}

结果:

duplicate:2

如果要找出所有的重复数字,也比较简单,只需要继续匹配,并把已经找出的重复数字记录到另外的数组中就可以了。

 

现在要求,不能改变原数组!!且数组的每个值在0~n-2中,也就意味着数组中必然有至少一个重复数字。

第一种做法:把原数组拷贝到新的数组之中,用副本的数组进行如上的操作。

第二种做法:用二分法进行查找。把0~n-2分为两部分:0~m和m~n-2,先匹配0~m在data数组中存在的次数,如果次数>m,则说明数组中在0~m之间有重复的数字,接下来再分为0~m/2和m/2到m。依次这样下去,直到找到最后的重复数字为止。

代码如下:

#include "../common.h"

/*计算在start至end的数字总共出现了多少次*/
int countRange(const int *dataNumbers,int length,int start,int end)
{
    if(dataNumbers == nullptr)
    {
        return 0;
    }
    int count = 0;
    for(int i = 0; i < length; ++i)
    {
        if(dataNumbers[i] <= end && dataNumbers[i] >= start)
        {
            ++count;
        }
    }
    return count;
}


bool findDuplicateNumbers(const int *dataNumbers, int length, int *duplicate)
{
    if(dataNumbers == nullptr)
    {
        return false;
    }
    for(int i = 0; i < length; ++i)
    {
        if(dataNumbers[i] < 0 || dataNumbers[i] > length - 2)
        {
            return false;
        }
    }
    int start = 0;
    int end = length - 2;

    while(end >= start)
    {
        int middle = ((end - start) >> 1) + start;
        int count = countRange(dataNumbers,length,start,middle);
        if(end == start) //最后一组
        {
            if(count > 1)
            {
                *duplicate = end;
                return true;
            }
            else
            {
                return false;
            }
            
        }

        if(count > (middle - start + 1)) //重复的数字,就在这半段继续二分法找
        {
            end = middle;
        }
        else  //上半段没有重复的,匹配另外半段
        {
            start = middle + 1;
        }
    }
    return false;
}

int main()
{
    int data[8] = {0,4,1,2,4,5,6,3};
    int duplicate = -1;
    if(findDuplicateNumbers(data,8,&duplicate) != false)
    {
        printf("duplicate:%d\n",duplicate);
    }
    else
    {
        printf("No duplicate data !!\n");
    }  
}

结果:

duplicate:4

这种二分法没有办法找出所有的重复数字!!!

如果不确定数组中是否有重复的数字,也就是把值限定在0~n-1中时,二分法查找重复数字是无效的。如当n为8时,数组为[0,1,3,3,4,5,6,7],数组中少了2,重复了3,但是无法用二分法查找出来。因为在这个数组中,本身就可以没有重复的数字。二分法只能用于确定\查找,无法用于判断。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值