【剑指Offer】面试题3 数字中重复的数字——题目二:不修改数组找出重复的数字(Java)

题目二:不修改数组找出重复的数字

在一个长度为n+1的数组中的所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的。在不修改输入数组的情况下找出数组中任意一个重复数字。例如输入长度为8的数组{2, 3, 5, 4, 3, 2, 6, 7},则对应输出的是2或者3。

 


题解1:

思路:创建一个长度为n+1的辅助数组,然后逐一把原数组的每个数字复制到辅助数组。如果原数组中被复制的数字是m,则把它复制到辅助数组中下标为m的位置,当发现该位置已有一个与下标相同的值时,就找到一个重复数字了。需要O(n)的辅助空间,时间复杂度为O(n)。

代码实现:

class Solution 
{
    public static void findTheRepeatNum(int []arr) 
    {

        int newArr[] = new int[arr.length+1];
        for (int i=0;i<arr.length;i++)
        {

            if (arr[i]!=newArr[arr[i]])
                newArr[arr[i]]=arr[i];
            else 
            {
                System.out.println(arr[i]);
                return;
            }
        }
    }
}

 


解法2:

思路:按照二分查找的思路,将数组中1~n的数字从中间数m分为两部分(是数字大小的中间,而非位置上的中间),前面一半为1~m,后面一半为m+1~n。如果数组中1~m的数字数目超过m,那么重复数字一定出现在这段区间内,反之,重复数字一定出现在m+1~n这段区间内。我们可以重复对包含重复数字的区间进行划分,直到找到这个重复数字。

以题目中的长度为8的数组{2,3,5,4,3,2,6,7}为例:

根据题意,该数组中所有数字都在1~7之间,所以用来划分区间的界限数m为4,将其分为1~4和5~7两个部分。

然后统计1~4四个数字在数组中出现的次数,一共出现5次,所以1~4四个数字中,一定有一个以上的重复数字。

接着再把1~4划分为{1,2},{3,4}两个部分,分别统计数字{1,2}和{3,4}出现的次数,前者两次,后者三次,所以,可以判断,3,4中一定有一个数重复了。

最后分别统计3和4在数组中出现次数,发现3出现了两次,所以找到了一个重复的数字3。

该解法的时间复杂度为O(nlogn),但空间复杂度仅为O(1),较之解法一,相当于是以时间换空间。

这种解法无法找出所有重复数字,因为[1,2]这个区间内有1,2两个数字,他们共出现了两次,但是1没有出现,2出现了两次,所以这里被默认为没有重复数字跳过了。

代码实现:

class Solution {
    public static void findTheRepeatNum(int []arr) {

        if (arr==null||arr.length<1)
            return;

        int length = arr.length;
        int begin = 1;
        int end = length-1;
        while (end>=begin)
        {
            int mid = ((end-begin)>>1)+begin;               //求出分界值
            int count = countRange(arr,length,begin,mid);   //统计次数
            if (end==begin)
            {   

                //当mid begin end三者重合时(区间内只有一个数字),判断出现次数是否大于1,                    
                if (count>1) {
                    System.out.println(begin);
                    return;
                }
                else
                    break;
            }

            //判断重复数字在哪一边
            if (count>(mid-begin+1)) //如果左边的数字出现的次数大于界限所有的数的数量       
                end=mid;                    //则将分界值作为右边界
            else
                begin=mid+1;                //否则将分界值后一个数作为左边界
        }
        return;
    }

    //统计出现的次数
    public static int countRange(int[]arr,int length,int begin,int end) 
    {

        if (arr==null)  //数组为空
            return 0;

        int count=0;

        for (int  i=0;i<length;i++)
            if (arr[i]>=begin&&arr[i]<=end) //统计区间内的数在数组中出现的次数
                count++;
            return count;
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值