287. 寻找重复数

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> 287. 寻找重复数

本题来自 LeetCode:287. 寻找重复数[1]

题目详情

给定一个包含 n + 1个整数的数组 nums,其数字都在 1 n之间(包括 1 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。

示例 1:


输入: [1,3,4,2,2]
输出: 2

示例 2:


输入: [3,1,3,4,2]
输出: 3

说明:

  • 不能更改原数组(假设数组是只读的)。
  • 只能使用额外的 `O(1)`的空间。
  • 时间复杂度小于`O(n^2)` 。
  • 数组中只有一个重复的数字,但它可能不止重复出现一次。
  • 题目分析

    暴力法

    若不考虑说明 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]

  • 初始数组范围为`[1, 5]`,中位数为`3`,数组中`=3`的数值个数为`4`,这就表明重复的数值在区间`[1, 3]`中。这样就将区间缩小一半了。
  • 缩小数组范围为`[1, 3]`, 中位数为`2`,数组中`=2`的的数值个数为`3`,这就表示重复的数值范围在区间`[1, 2]`中,继续将区间缩小一半。
  • 缩小数组范围为`[1, 2]`, 中位数为`1`,数组中`=1`的的数值个数为`1`,这就表示重复的数值范围在区间`[2, 2]`中,进得到重复数为`2`。
    时间复杂度`O(nlog(n))`,空间复杂度`O(1)`。
  • 
    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;
        }
    }
    

    参考资料

    1. 寻找重复数: https://leetcode-cn.com/problems/find-the-duplicate-number/

    原文始发于微信公众号(xiaogan的技术博客):

    本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

    本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

    原文链接:blog.ouyangsihai.cn >> 287. 寻找重复数


      转载请注明: 好好学java 287. 寻找重复数

     上一篇
    152. 乘积最大子序列 152. 乘积最大子序列
    本题来自 LeetCode:152. 乘积最大子序列[1]。 题目详情给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数)。 示例 1: 输入: [2,3,-2,4] 输出: 6 解释: 子数组 [2,
    2021-04-05
    下一篇 
    LeetCode(121&53) 买卖股票的最佳时机&最大子序和 LeetCode(121&53) 买卖股票的最佳时机&最大子序和
    121. 买卖股票的最佳时机本题来自LeetCode:121. 买卖股票的最佳时机 题目详情给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的
    2021-04-05