数组中重复的数字-剑指offer面试题3

找出数组中重复的数字

问题描述:

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

方法一:排序做法

将数组进行排序,排序后再查找重复数字。

class Solution {
    public int findRepeatNumber(int[] nums) {
            Arrays.sort(nums);
            int pre = nums[0];
            for(int index=1; index < nums.length; index++){
                if(pre == nums[index])
                    return pre;
                pre = nums[index];
            }
        return -1;
    }
}

时间复杂度:O(nlogn),空间复杂度:O(1)

方法二:哈希表

从头到尾按顺序扫描数组的每个数字,每扫描到一个数字的时候,都可以用O(1)的时间来判断哈希表里是否存在这个数字,如果不存在的话,就把该数字加入到这个哈希表中,如果存在的话就说明该数字重复。

class Solution {
    public int findRepeatNumber(int[] nums) {
        int n = nums.length;
        Set<Integer> set = new HashSet<Integer>();
        for(int index=0;index<n;index++){
            if(set.contains(nums[index]))
                return nums[index];
            set.add(nums[index]);
        }
        return -1;
    }
}

时间复杂度:O(n),空间复杂度:O(n)

方法三:原地置换法

即书中所提到的方法:当扫描到下标为i的数字时,首先比较这个数字(用m表示)是否等于i,如果相等,扫面下一个数字;如果不相等,将它与下标为m的数组中数字进行比较,如果相等就说明该数字时重复的(再下标和im的位置都出现了),如果不相等,就把下标为i(值为m)与下标为m的数组中的两个数进行互换,把m放到属于他的位置。接下来重复这个比较、交换的过程,直到我们发现一个重复的数字。

class Solution {
    public int findRepeatNumber(int[] nums) {
        int n = nums.length;
        for(int index=0;index<n;index++){
            while(nums[index] != index){
                if(nums[index] == nums[nums[index]])
                    return nums[index];
                 int temp = nums[index];
                 nums[index] = nums[temp];
                 nums[temp] = temp;
            }
        }
        return -1;
    }
}

小心错误交换方式:num[index]的值再第二行时就已经改变了

				int temp = nums[index];
                nums[index] = nums[nums[index]];
                nums[nums[index]] = temp;

时间复杂度:O(n),空间复杂度:O(1)。时间复杂度解释:代码中经管有一个二重循环,但每个数字最多两次交换就能找到属于它的位置。
总结:第一种方式最简单明了,但是时间复杂度最高,空间复杂度较低;第二种方法时间复杂度较低,但是空间复杂度最高;第三种方式时间复杂度和空间复杂度都是最低的,也就是书中的方法。

这题是不能用二分解的,原因就出在数组的长度上面,因为278题长度n+1,1 ~ n代表必定有一个数字出现两次,所以你用二分有个区间的数字在原数组中出现次数是>这个区间范围的,但是对这题而言,长度n,0 ~ n-1且有重复,代表有数重复,就有数不存在,这样长度才符合,这将会导致可能重复的数和不存在的数在同一个区间你无法找出来。

不修改数组找到重复数字

问题描述:

力扣链接
Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.

原地置换法

由于不能修改数组,可以使用一个辅助数组来存储上述的数组,然后再用原地置换法解决问题,这种方法的时间复杂度为O(n),空间复杂度为O(n)。参考上面的代码即可。

二分查找法

将1 ~ n的数字从中间的数字m分为两个部分,前面一半是1 ~ m,后面的一半为m+1 ~ n,如果1 ~ m中的数字的个数超过了m,那么这一区间一定重复的数字,否则,另一个区间里一定会有重复的数字。将有重复的数字的区间再一分为二,直到找到哪个重复的数字。

class Solution {
    public int findDuplicate(int[] nums) {
        int n = nums.length;
        int start = 1;
        int end = n -1;
        while (end >= start) {
            int middle = ((end - start) >> 1) + start;
            int count = countRange(nums,start,middle);
            if (end == start) {
                if (count > 1)
                    return start;
                else
                    break;
            }
            if (count > (middle - start + 1)) {
                end = middle;
            } else {
                start = middle + 1;
            }
        }
        return -1;
    }

    int countRange(int[] nums, int start, int end) {
        int count = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] >= start && nums[i] <= end)
                count++;
        }
        return count;
    }
}

输入长度为n的数组,那么函数countRange将被调用O(logn)次,每次需要O(n)的时间。所以该方法的时间复杂度为:O(nlogn),空间复杂度为O(1)。时间换空间。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章