欢迎并且感谢交流区探讨和指正
题目
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。
数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。 请找出数组中任意一个重复的数字。
输入:[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
方法一:遍历交换法(最优解)
时间复杂度:O(n)
空间复杂度:O(1)
分析
首先要明确一个事,输入数组 nums 的长度为 n,数组下标为 0 ~ n-1,且数组中的数的范围是 0 ~ n-1
- 若数组中没有重复数字,将数组排序后,数组中存储的值将与其下标值一一对应相等
- 当数组中有重复数字时,此时将数组排序,必有一个现象:最少存在一个重复的值不仅存在于其对应的下标处,还存在于别处
该方法正是利用了这个规律,解题思路如下
从头开始扫描数组,先判断该位置的数是否与下标相等,即该数是否在排序后它应该在的位置
- 若相等,i++ 继续判断下一个
- 若不相等,即 nums[i] != i,此时应该让它去它该去的地方,但是那个位置上是否已经存在重复值了呢?判断 nums[nums[i]] 与 nums[i] 是否相等
- 若相等,存在重复值,结束
- 若不相等,将二者交换,并对交换回来的数再次从头进行判断
只需要一次遍历,所以时间复杂度为 O(n)
需要有限个临时变量,所以空间复杂度为 O(1)
代码
/**
* @param {number[]} nums
* @return {number}
*/
var findRepeatNumber = function (nums) {
var i = 0;
while (i < nums.length) {
if (nums[i] === i) i++
else if (nums[nums[i]] != nums[i]) {
var temp = nums[nums[i]]
nums[nums[i]] = nums[i]
nums[i] = temp
} else {
return nums[i]
}
}
return -1
};
方法二:哈希表法
时间复杂度:O(n)
空间复杂度:O(n)
分析
只需要一次遍历,所以时间复杂度为 O(n)
需要一个长度为 n 的哈希表存储数据,所以空间复杂度为 O(n)
代码
var findRepeatNumber = function (nums) {
var arr = new Array(nums.length)
for (var i = 0; i < nums.length; i++) {
if (arr[nums[i]] === 1) {
return nums[i]
} else {
arr[nums[i]] = 1
}
}
};
方法三:先排序后遍历
时间复杂度:O(nlgn)
空间复杂度:O(1)
分析
先将数组排序,时间复杂度为 O(nlgn)
然后从头遍历找到重复数字,时间复杂度为 O(n)
代码
var findRepeatNumber = function (nums) {
nums.sort()
for (var i = 1; i < nums.length; i++) {
if (nums[i] === nums[i - 1]) {
return nums[i]
}
}
return -1
};
拓展
在一个长度为 n+1 的数组 nums 里的所有数字都在 0~n 的范围内。
所以数组中至少有一个数字是重复的。(抽屉原理)
请找出数组中任意一个重复的数字,但不能修改原数组。
输入:[2, 3, 5, 4, 3, 2, 6, 7]
输出:2 或 3
分析
复习一下二分查找