赞
踩
给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
示例 1:
输入: [1,3,4,2,2]
输出: 2
示例 2:
输入: [3,1,3,4,2]
输出: 3
说明:
不能更改原数组(假设数组是只读的)。
只能使用额外的 O(1) 的空间。
时间复杂度小于 O(n2) 。
数组中只有一个重复的数字,但它可能不止重复出现一次。
class Solution {
public int findDuplicate(int[] nums) {
Arrays.sort(nums);
for (int i = 1; i < nums.length; i++) {
if (nums[i] == nums[i-1]) {
return nums[i];
}
}
return -1;
}
}
class Solution {
public int findDuplicate(int[] nums) {
Set<Integer> seen = new HashSet<Integer>();
for (int num : nums) {
if (seen.contains(num)) {
return num;
}
seen.add(num);
}
return -1;
}
}
复制一段评论里对官方题解对方法三的说明:
一般而言,面试官会更注重基础、常见的知识的考察。我个人觉得按照 :1、哈希表判重,2、再说排序以后相邻元素相等即找到的重复;3、桶排序;4、二分法。然后分析一下这些方法的时间复杂度、空间复杂度,做一个简单比较,我觉得应该就很不错了。我是不会讲方法三的,感觉面试的时候说方法三是给自己挖坑。
此方法是将nums的value作为下一个要去的下标值
先用快慢指针找到相交点,然后其中一个重新指向首节点,两个指针同时走,再次相遇时即为循环首节点
tortoise和hare不一定会在重复数字处相遇,比如[2,5,9,6,9,3,8,9,7,1]
或许可以再优化一下,如果相遇的时候正好index也不同,就直接是重复元素了
class Solution { public int findDuplicate(int[] nums) { // Find the intersection point of the two runners. int tortoise = nums[0]; int hare = nums[0]; do { tortoise = nums[tortoise]; hare = nums[nums[hare]]; } while (tortoise != hare); // Find the "entrance" to the cycle. int ptr1 = nums[0]; int ptr2 = tortoise; while (ptr1 != ptr2) { ptr1 = nums[ptr1]; ptr2 = nums[ptr2]; } return ptr1; } }
class Solution { public int findDuplicate(int[] nums) { // Find the intersection point of the two runners. int tortoise = nums[nums[0]]; int hare = nums[nums[nums[0]]]; while (tortoise != hare) { tortoise = nums[tortoise]; hare = nums[nums[hare]]; } // Find the "entrance" to the cycle. int ptr1 = nums[0]; int ptr2 = tortoise; while (ptr1 != ptr2) { ptr1 = nums[ptr1]; ptr2 = nums[ptr2]; } return ptr1; } }
以 [1, 2, 2, 3, 4, 5, 6, 7] 为例,一共 8 个数,n + 1 = 8,n = 7,根据题目意思,每个数都在 1 和 7 之间。
例如:区间 [1, 7] 的中位数是 4,遍历整个数组,统计小于等于 4 的整数的个数,至多应该为 4 个。换句话说,整个数组里小于等于 4 的整数的个数如果严格大于 4 个,就说明重复的数存在于区间 [1, 4],它的反面是:重复的数存在于区间 [5, 7]。
于是,二分法的思路是先猜一个数(有效范围 [left, right]里的中间数 mid),然后统计原始数组中小于等于这个中间数的元素的个数 cnt,如果 cnt 严格大于 mid,(注意我加了着重号的部分“小于等于”、“严格大于”)依然根据抽屉原理,重复元素就应该在区间 [left, mid] 里。
public class Solution { public int findDuplicate(int[] nums) { int len = nums.length; int left = 1; int right = len - 1; while (left < right) { // 在 Java 里可以这么用,当 left + right 溢出的时候,无符号右移保证结果依然正确 int mid = (left + right + 1) >>> 1; int cnt = 0; for (int num : nums) { if (num < mid) { cnt += 1; } } // 根据抽屉原理,严格小于 4 的数的个数如果大于等于 4 个, // 此时重复元素一定出现在 [1, 3] 区间里 if (cnt >= mid) { // 重复的元素一定出现在 [left, mid - 1] 区间里 right = mid - 1; } else { // if 分析正确了以后,else 搜索的区间就是 if 的反面 // [mid, right] // 注意:此时需要调整中位数的取法为上取整 left = mid; } } return left; } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。