题目描述
给你一个整数数组 nums ,该数组具有以下属性:
nums.length == 2 * n.
nums 包含 n + 1 个 不同的 元素
nums 中恰有一个元素重复 n 次
找出并返回重复了 n 次的那个元素。
示例 1:
输入:nums = [1,2,3,3]
输出:3
示例 2:
输入:nums = [2,1,2,5,3,2]
输出:2
示例 3:
输入:nums = [5,1,5,2,5,3,5,4]
输出:5
提示:
2 <= n <= 5000
nums.length == 2 * n
0 <= nums[i] <= 104
nums 由 n + 1 个 不同的 元素组成,且其中一个元素恰好重复 n 次
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/n-repeated-element-in-size-2n-array
题目本身非常简单,看了各种题解发现有很多不一样的思路,很有意思。
1. 哈希表统计
根据题目描述,我们知道重复的这个数,出现的次数是数组长度的一半,其他每个数都只会出现一次,所以实际上我们只要遍历数组,一旦发现有重复数字出现时,则就是我们要找的数字。
class Solution {
public int repeatedNTimes(int[] nums) {
Map<Integer, Integer> map = new HashMap<>();
for(int num : nums){
int val = map.getOrDefault(num, 0) + 1;
map.put(num, val);
if(val > 1){
return num;
}
}
return -1;
}
}
换成set
集合,更方便
class Solution {
public int repeatedNTimes(int[] nums) {
Set<Integer> set = new HashSet<>();
for (int num : nums) {
if (!set.add(num)) {
return num;
}
}
return -1;
}
}
复杂度分析
时间复杂度:O(n)
空间复杂度:O(n)
2. 随机选择
官方提供的一种解法,思路非常简单,每次随机选择两个不同的下标,判断它们对应的元素是否相等即可。如果相等,那么返回任意一个作为答案。
public int repeatedNTimes(int[] nums) {
Random random = new Random();
int n = nums.length;
while (true) {
int r1 = random.nextInt(n);
int r2 = random.nextInt(n);
if (r1 != r2 && nums[r1] == nums[r2]) {
return nums[r1];
}
}
}
复杂度分析
双O(1)的复杂度
时间复杂度:期望O(1),选到两个相同元素的概率为:n/2n * n-1/2n ≈ 1/4(假设n为5,则数组长度为10,那么也就是50% * 40% ≈ 0.2,n越大,结果越趋近 0.25),因此期望查找4次即可完成。
空间复杂度:O(1)
3. 数学
也是官方提供的一种方式,通过数学公式推导出来,不过还是比较简单的,可以看的懂。。。
首先假设重复元素x在数组中出现的位置,如果存在每个x相邻的位置之间都至少
间隔了两个其他元素,那么数组的总长度至少
为:n+(n-1)*2 = 3n-2
题目中给出了n的范围:2 <= n <= 5000
以及数组的长度:nums.length == 2 * n
所以当n为2时,则需满足3n-2 > 2n
,很明显不成立,所以一定存在两个相邻的x位置是连续的,或者只间隔了1位。
比如像下面这样:
x = 1,n = 2
1 2 3 1
当n超过2,则无法满足要求了
1 2 3 1 后面还需要再加两个数,且必须有一个数是1。
那么肯定不符合每个x相邻的位置之间都至少间隔了两个其他元素
所以,这样一来,我们只需要遍历所有间隔2个以及2个以内位置的下标对(当n=2时,数组的长度最多为4,因此最多只能隔2个位置。),判断对应的元素是否相对即可。
public int repeatedNTimes(int[] nums) {
for (int gap = 1; gap <= 3; gap++) {
for (int i = 0; gap + i < nums.length; i++) {
if(nums[i] == nums[gap + i]){
return nums[i];
}
}
}
return -1;
}
复杂度分析
时间复杂度:期望O(n),最多遍历3次
空间复杂度:O(1)