[LeetCode] 287. Find the Duplicate Number

Given an array of integers nums containing n + 1 integers where each integer is in the range [1, n] inclusive.

There is only one repeated number in nums, return this repeated number.

You must solve the problem without modifying the array nums and uses only constant extra space.

Example 1:

Input: nums = [1,3,4,2,2]
Output: 2

Example 2:

Input: nums = [3,1,3,4,2]
Output: 3

Constraints:

  • 1 <= n <= 105
  • nums.length == n + 1
  • 1 <= nums[i] <= n
  • All the integers in nums appear only once except for precisely one integer which appears two or more times.

Follow up:

  • How can we prove that at least one duplicate number must exist in nums?
  • Can you solve the problem in linear runtime complexity?

寻找重复数。

给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。

假设 nums 只有 一个重复的整数 ,返回 这个重复的数 。

你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/find-the-duplicate-number
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题意是在一个数组中找到重复的数字。数组包含 N + 1 个数字,数字在 1 <= x <= n 之间。两种解法。

1. 二分法。注意数组是无序的。当得到数组长度 len 和数组长度的一半 mid 之后,用 count 记录有多少个数小于等于中间数 mid。举个例子,如果数组长度是 10,mid 则是 5,这里 mid 指的是数组长度的一半。如果小于 mid 的个数大于数组的一半,说明重复的数字一定是小于 mid 的;反之如果大于 mid 的个数过半,重复的数字一定是大于 mid 的。用二分法逐渐逼近这个值,注意 return 的是 left。

时间O(nlogn) - 二分 logn * 每次找到 mid 之后要再把所有元素看一遍

空间O(1)

Java实现

 1 class Solution {
 2     public int findDuplicate(int[] nums) {
 3         int len = nums.length;
 4         // 因为数字的范围就是从1开始的,所以这里不是0,是1
 5         int left = 1;
 6         int right = len - 1;
 7         while (left <= right) {
 8             // 在 Java 里可以这么用,当 left + right 溢出的时候,无符号右移保证结果依然正确
 9             int mid = left + (right - left) / 2;
10             int count = 0;
11             for (int num : nums) {
12                 if (num <= mid) {
13                     count++;
14                 }
15             }
16             // 根据抽屉原理,小于等于 4 的个数如果严格大于 4 个
17             // 此时重复元素一定出现在 [1, 4] 区间里
18             if (count > mid) {
19                 // 重复元素位于区间 [left, mid]
20                 right = mid - 1;
21             } else {
22                 // if 分析正确了以后,else 搜索的区间就是 if 的反面
23                 // [mid + 1, right]
24                 left = mid + 1;
25             }
26         }
27         return left;
28     }
29 }

 

JavaScript实现

 1 /**
 2  * @param {number[]} nums
 3  * @return {number}
 4  */
 5 var findDuplicate = function(nums) {
 6     let start = 1;
 7     let end = nums.length - 1;
 8     while (start < end) {
 9         let middle = Math.floor((start + end) / 2);
10         let count = 0;
11         // 计算总数组中有多少个数小于等于中间数
12         for (let i = 0; i < nums.length; i++) {
13             if (nums[i] <= middle) {
14                 count++;
15             }
16         }
17 
18         if (count <= middle) {
19             start = middle + 1;
20         } else {
21             end = middle;
22         }
23     }
24     return start;
25 };

 

2. 快慢指针。这个思路很难想到,参考142题。因为数组一定是有重复数字出现而且数字的范围不大于数组的长度,所以可以用快慢指针的思路做。当快慢指针相遇之后,再走第二遍,再次相遇的地方就是环的起点,也就是重复的数字。

时间O(n)

空间O(1)

Java实现

 1 class Solution {
 2     public int findDuplicate(int[] nums) {
 3         int len = nums.length;
 4         if (len > 1) {
 5             int slow = nums[0];
 6             int fast = nums[nums[0]];
 7             while (slow != fast) {
 8                 slow = nums[slow];
 9                 fast = nums[nums[fast]];
10             }
11             slow = 0;
12             while (slow != fast) {
13                 slow = nums[slow];
14                 fast = nums[fast];
15             }
16             return slow;
17         }
18         return -1;
19     }
20 }

 

JavaScript实现

 1 /**
 2  * @param {number[]} nums
 3  * @return {number}
 4  */
 5 var findDuplicate = function(nums) {
 6     const len = nums.length;
 7     if (len > 0) {
 8         let slow = nums[0];
 9         let fast = nums[nums[0]];
10         while (slow !== fast) {
11             slow = nums[slow];
12             fast = nums[nums[fast]];
13         }
14         slow = 0;
15         while (slow !== fast) {
16             slow = nums[slow];
17             fast = nums[fast];
18         }
19         return slow;
20     }
21     return -1;
22 };

 

LeetCode 题目总结

posted @ 2019-10-23 02:00  CNoodle  阅读(174)  评论(0编辑  收藏  举报