【剑指offer】3. 数组中重复的数字

题目描述

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

示例 1:

输入:[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3

限制:
2 <= n <= 100000

解题思路

本题不仅考查coding能力,还考查应聘者的沟通能力,应与面试官确定是否可以修改原属组,以及时间和空间的要求。如果可以修改原数组,则直接利用数组下标进行元素替换;如果不能修改原数组,则考虑时间和空间的tradeoff,下面具体讲。

1. 暴力搜索

先排序,然后遍历数组,直到找到重复元素。
暴力算法的时间复杂度O(nlogn)(数组排序的时间复杂度)。

2. 可修改原数组的算法

遍历数组,遇到下标为i的数字m,首先将m与下标i进行比较,若相等,则继续遍历下一个数字;若不相等,则将数字m与num[m](即下标为m的数字)进行比较,若m==num[m],则说明发现重复数字;若不相等,则将数字m与num[m]进行交换,讲数字m放到属于它的位置上。

class Solution {
    public int findRepeatNumber(int[] nums) {
        if(nums==null || nums.length<=0)
            return -1;
            
        int len = nums.length;
        
        for(int i=0; i<len; i++){
            if(nums[i]==i){
                continue;
            }
            else if(nums[i]==nums[nums[i]]){
                return nums[i];
            }
            else{
                int temp = nums[i];
                nums[i] = nums[temp];
                nums[temp] = temp;
            }
        }

        return -1;
    }
}

3. 不可修改原数组的算法

(1)辅助空间

一种空间换时间的方法是,使用O(n)的辅助空间,创建一个长度为n+1的辅助数组,然后逐一把原数组的每个元素复制到辅助数组。同时,如果原数组的数字为m,则将其复制到新数组下标为m的位置。这样很容易就可以找到重复数字。

(2)二分查找

将1~n的数字从中间的数字m分为2部分,第一部分是1~m,第二部分是m+1~n。如果1~m数字的个数超过m个,则这个区间内一定含有重复数字。不过这个方法不能保证找出所有重复数字,每次countRange()方法时,只能统计在start~end区间内的数字个数。

注意:此方法无法找到所有重复的数字,无法通过leetcode所有测试用例。
例如:[0, 1, 2, 0, 4, 5, 6, 7, 8, 9],在计数0~4的count时count=5,与数字数目相等,因此下一轮变为start = mid+1,导致输出结果错误。

class Solution {
    public int findRepeatNumber(int[] nums) {
        if(nums==null || nums.length<=0)
            return -1;

        int len = nums.length;
        int start = 0;  //数字0,不是下标
        int end = len-1;

        while(start<=end){
            int mid = ((end-start)>>1)+start;
            int count = countRange(nums, start, mid);
            if(start==end){ //只剩一个元素了
                if(count>1)
                    return start;
                else 
                    break;
            }

            if(count>(mid-start+1))
                end = mid;
            else
                start = mid+1;
        }
        return -1;
    }

    public int countRange(int[] nums, int start, int end){
        if(nums==null)
            return 0;
        int len = nums.length;
        int count = 0;

        for(int i=0; i<len; i++){
            if(nums[i]>=start && nums[i]<=end)
                count++;
        }

        return count;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值