剑指offer 面试题3 题目二(Java代码)

面试题3:数组中重复的数字
题目二:不修改数组找出重复的数字
在一个长度为n+1的数组里的所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。例如,如果输入长度为8的数组{2,3,5,4,3,2,6,7},那么对应的输出是重复的数字2或者3。

解决方案1:
题目要求不能修改输入的数组,可以创建一个长度为n+1的辅助数组,然后逐一把原数组的每个数字复制到辅助数组。如果原数组中被复制的数字是m,那么就把它复制到辅助数组下标为m的位置。比较简单,但是空间复杂度是O(n);

优化方案:(空间复杂度n(1) 时间复杂度O(nlogn) )
数组长度为n+1,数组里所有的数字都在1~n的范围内,数组里总共有大于n个数,所以一定包含重复数字。
把从1~n的数字从中间的数字m分为两部分,前面一半为1到m,后面一半为m+1到n。
如果数组中1到m的数目超过m,就说明这一半的区间里一定包含有重复数字;否则,另一半m+1~n的区间里一定包含重复的数字。我们可以继续把包含重复数字的区间一分为二,直到找到一个重复的数字。

以【2 3 5 4 3 2 6 7】为例,8个数字,数组中所有数字都在1~7的范围内。中间的数字是4,把1到7分为两个范围,分别是1到4、5到7,然后统计数组中的数在1到4这个区间出现的次数,一共出现了五次,大于四,所以1到4这个区间一定存在重复数字。
再把1~4一分为二,1到2、3到4。数字1或2在数组中一共出现了两次,再统计数字3或者4在数组中出现的次数,他们一共出现了三次,意味着3到4中肯定有重复的数字。再分别统计这两个数在数组中出现的次数。最后发现3出现了两次,确定是重复数字。

 class Solution {
   
    public static int f(int[] arr,int len) {
        if (arr == null || len <= 0) return -1;
        
        //从1到n
        int start = 1;
        int end = len - 1;
        while (end >= start) {
            int mid = ((end - start) >> 1) + start;
            int count = countRange(arr, len, start, mid);
            if (end == start) {  //当起始数跟结束数相同时
                if (count > 1) return start;  //count大于一表示start这个数在数组中重复(不止一个)
                else break;
            }
            if (count > (mid - start) + 1) {
                end = mid; //当count大于n的中间数时,说明在1到n/2中间有重复数,将end=mid进行递归
            } else {
                start = mid + 1; //当count小于n的中间数时,说明在(n/2) + 1到n中间有重复数
            }
        }
        return -1;
    }
    
    //统计有多少个数是在start 到end这个范围内的
    public static int countRange(int[] arr,int len,int start,int end) {
        if(arr == null) return 0;
       
        int count = 0;
        for(int i = 0; i < len; i++) {
            if(arr[i] >= start && arr[i] <= end) {
                ++count;
            }
        }
        return count;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值