本题来自 LeetCode:287. 寻找重复数[1]
题目详情
给定一个包含
n + 1个整数的数组
nums,其数字都在
1到
n之间(包括
1和
n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
示例 1:
输入: [1,3,4,2,2]
输出: 2
示例 2:
输入: [3,1,3,4,2]
输出: 3
说明:
题目分析
暴力法
若不考虑说明
3的内容,可以直接用暴力法解答出来。暴力法只需要两个循环进行比较即可。
时间复杂度
O(n^2),空间复杂度
O(1)。
class Solution {
    public int findDuplicate(int[] nums) {
        for(int i = 0; i nums.length; i ++) {
            for(int j = i + 1; j nums.length; j ++) {
                if(nums[i] == nums[j]) {
                    return nums[i];
                }
            }
        }
        throw new RuntimeException();
    }
}
替换法(抽屉原理)
若不考虑说明
1的内容,可以通过替换法解答。核心思想是将数值
i + 1放置到数组
nums的
i位置上 。若该位置已放置正确的数值,则该数值就是重复的。
时间复杂度
O(n),空间复杂度
O(1)。
class Solution {
    public int findDuplicate(int[] nums) {
        for(int i = 0; i nums.length; i ++) {
            while(nums[i] != i + 1) {
                if(nums[nums[i] - 1] == nums[i]) {
                    return nums[i];
                }
                swap(nums, nums[i] - 1, i);
            }
        }
        throw new RuntimeException();
    }
    public void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}
二分法
考虑到题目要求不能更改原数组,且时间复杂度小于
O(n^2),因此暴力法和替换法均不满足要求。
采用二分法解答的关键:对要定位的
中位数做二分,而不是对数组索引做二分。要定位的
中位数的范围为 
[1, n],这样每一次二分都可以将搜索区间缩小一半。
比如数组
[1,3,4,2,2]。
class Solution {
    public int findDuplicate(int[] nums) {
        int left = 1;
        int right = nums.length;
        while(left  right) {
            int mid = (left + right)  1;
            int count = 0;
            for(int i = 0; i  nums.length; i ++) {
                if(nums[i] = mid) count ++;
            }
            if(count  mid) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }
}
参考资料
原文始发于微信公众号(xiaogan的技术博客):
 本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!
        本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!
     
                        
                        