题目
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例 1:
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
限制:
2 <= n <= 100000
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
方法一:哈希表
我们可以利用哈希表中的Count函数来快速的找到数组元素nums[i]是否出现。一次遍历中,如果元素未曾出现,则将该元素加入哈希表;如果已经出现,直接返回重复元素即可。
由于题目说,存在多个重复元素,只需找到任意一个,我们的这种方法找到的是第一个出现的重复元素。
AC代码

时空复杂度
经典空间换时间。
时间复杂度:最坏情况,也就是重复元素在最后一位,此时为O(N)。
空间复杂度:同上,O(N)。
方法二:数组排序
要想在不开辟额外的内存空间情况下,实现题目要求,我们可以先对数组进行一个排序(题目没说不能改变原数组),之后遍历排序数组,遇到重复元素则返回即可。
AC代码

时空复杂度
时间复杂度:排序的平均时间复杂度为O(NLogN)
空间复杂度:O(1)
方法三:辅助数组
在方法二中,我们使用了排序的方法先对数组进行预处理。其实,我们还有个题目条件没有用到:数组 nums 里的所有数字都在 0~n-1 的范围内。根据这个信息,我们可以想到一个新方法:
开辟一个长度为n的数组res,初始化所有元素为0
对nums遍历,nums[i]作为下标pos,将res中pos对应的值加一,也就是res[pos]++
退出条件:如果res[pos]为1,说明已有重复元素。
AC代码

时空复杂度
时间复杂度:O(N)
空间复杂度:O(N)
方法四:辅助数组进阶(原地)
我们可以将前三种方法对应的时空复杂度列表对比:
方法 | 哈希表 | 排序 | 辅助数组 |
时间复杂度 | O(N) | O(NlogN) | O(N) |
空间复杂度 | O(N) | O(1) | O(N) |
可以看到,排序的时间复杂度最高,但是空间复杂度最低。有没有一种方法,可以做到又快又省内存呢?
答案是可以。
依然是盯着:数组 nums 里的所有数字都在 0~n-1 的范围内,这个重要信息。我们可不可以将元素放到数组中正确的位置中去? 例如:
输入:
[2, 3, 1, 0, 2, 5, 3]
数组下标为0的元素为2 而数组下标为2的元素为1。 我们将其交换,就把元素2放到了下标正确的位置:
[1, 3, 2, 0, 2, 5, 3]
此时,数组下标为0的元素为1,而数组下标为1的元素为3。 继续交换,就把元素1放到了下标正确的位置:
[3, 1, 2, 0, 2, 5, 3]
如此循环,直到待交换的元素和下标对应位置上的元素重复,即nums[nums[i]]==nums[i] 则说明我们已经找到了重复元素。
AC代码

时空复杂度
时间复杂度:看似有两个while循环,实际上的时间复杂度为O(N)
空间复杂度:原地操作,O(1)
备注
对上述四种方法。如果题目没有说到数组元素在0~n-1之间,就只能使用哈希表或者排序方法。
或者如果不能对数组进行改变,则只能使用辅助空间的方法。大家可以自己随机应变。