287、寻找重复数
给定一个包含 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) 。
- 数组中只有一个重复的数字,但它可能不止重复出现一次。
难度:中等 题目地址:https://leetcode-cn.com/problems/find-the-duplicate-number/
1、C语言代码:
//解法一:暴力法
int findDuplicate(int* nums, int numsSize){
for(int i = 0;i < numsSize - 1;i++){
for(int j = i + 1;j < numsSize;j++){
if(nums[i] == nums[j])
return nums[i];
}
}
return;
}
//解法二:排序法
int cmp(const void *a, const void *b){
return (*(int *)a - *(int *)b);
}
int findDuplicate(int* nums, int numsSize){
qsort(nums, numsSize, sizeof(int), cmp);
for (int i = 0; i < numsSize - 1; i++) {
if (nums[i] == nums[i + 1]) {
return nums[i];
}
}
return;
}
解释: 方法一暴力求解,双重循环判断是否有重复数。
方法二:如果对数字进行排序,则任何重复的数字都将与排序后的数组相邻。
知识点回顾: 无。
2、Java代码:
//解法一:集合
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;
}
}
//解法二:快慢指针法
class Solution {
public int findDuplicate(int[] nums) {
int length = nums.length;
if (length > 1) {
// 找到快慢指针相遇的地方
int slow = nums[0];
int fast = nums[nums[0]];
while (fast != slow) {
slow = nums[slow];
fast = nums[nums[fast]];
}
// 用一个新指针从头开始,直到和慢指针相遇
fast = 0;
while (fast != slow) {
slow = nums[slow];
fast = nums[fast];
}
return slow;
}
return -1;
}
}
解释: 方法一:如果我们在数组上迭代时存储每个元素,我们可以在数组上迭代时简单地检查每个元素。为了实现线性时间复杂性,我们需要能够在恒定时间内将元素插入数据结构(并查找它们)。set 很好地满足这些约束,所以我们迭代数组并将每个元素插入 seen 中。在插入之前,我们检查它是否已经存在。如果是,那么我们找到了我们的副本,所以我们返回它。
方法二:假设数组中没有重复,那我们可以做到这么一点,就是将数组的下标和1到n每一个数一对一的映射起来。比如数组是213,则映射关系为0->2, 1->1, 2->3。假设这个一对一映射关系是一个函数f(n),其中n是下标,f(n)是映射到的数。如果我们从下标为0出发,根据这个函数计算出一个值,以这个值为新的下标,再用这个函数计算,以此类推,直到下标超界。实际上可以产生一个类似链表一样的序列。比如在这个例子中有两个下标的序列,0->2->3。
但如果有重复的话,这中间就会产生多对一的映射,比如数组2131,则映射关系为0->2, {1,3}->1, 2->3。这样,我们推演的序列就一定会有环路了,这里下标的序列是0->2->3->1->1->1->1->…,而环的起点就是重复的数。
所以该题实际上就是找环路起点的题。过程是这样的:我们先用快慢两个下标都从0开始,快下标每轮映射两次,慢下标每轮映射一次,直到两个下标再次相同。这时候保持慢下标位置不变,再用一个新的下标从0开始,这两个下标都继续每轮映射一次,当这两个下标相遇时,就是环的起点,也就是重复的数。
知识点回顾: 无。
3、Python代码:
class Solution:
def findDuplicate(self, nums: List[int]) -> int:
return int(abs(sum(nums)-sum(set(nums)))/abs(len(nums)-len(set(nums))))
解释: 重复数字是原始数组和求集合后的差值除以长度差。
知识点回顾: 无。
4、JavaScript代码:
/**
* @param {number[]} nums
* @return {number}
*/
var findDuplicate = function(nums) {
for(let i = 0;i < nums.length;i++){
if(nums.indexOf(nums[i]) !== nums.lastIndexOf(nums[i])){
return nums[i]
}
}
};
解释: 直接判断。
知识点回顾:
1、indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。
如果没有找到匹配的字符串则返回 -1。
注意: indexOf() 方法区分大小写。
语法:string.indexOf(searchvalue,start)
searchvalue:必需。规定需检索的字符串值。
start:可选的整数参数。规定在字符串中开始检索的位置。它的合法取值是 0 到 string Object.length - 1。如省略该参数,则将从字符串的首字符开始检索。
返回值:查找指定字符串第一次出现的位置,如果没找到匹配的字符串则返回 -1。
2、lastIndexOf() 方法可返回一个指定的字符串值最后出现的位置,如果指定第二个参数 start,则在一个字符串中的指定位置从后向前搜索。
注意: 该方法将从后向前检索字符串,但返回是从起始位置 (0) 开始计算子字符串最后出现的位置。 看它是否含有字符串。开始检索的位置在字符串的 start 处或字符串的结尾(没有指定 start 时)。
如果没有找到匹配字符串则返回 -1 。
注意:lastIndexOf() 方法是区分大小写的!
语法:string.lastIndexOf(searchvalue,start)
searchvalue:必需。规定需检索的字符串值。
start:可选的整数参数。规定在字符串中开始检索的位置。它的合法取值是 0 到 stringObject.length - 1。如省略该参数,则将从字符串的最后一个字符处开始检索。
返回值:查找的字符串最后出现的位置,如果没有找到匹配字符串则返回 -1。