标题:在长度 2N 的数组中找出重复 N 次的元素
出处:961. 在长度 2N 的数组中找出重复 N 次的元素
2 级
给你一个具有以下性质的整数数组 nums \texttt{nums} nums:
返回重复了 n \texttt{n} n 次的元素。
示例 1:
输入:
nums
=
[1,2,3,3]
\texttt{nums = [1,2,3,3]}
nums = [1,2,3,3]
输出:
3
\texttt{3}
3
示例 2:
输入:
nums
=
[2,1,2,5,3,2]
\texttt{nums = [2,1,2,5,3,2]}
nums = [2,1,2,5,3,2]
输出:
2
\texttt{2}
2
示例 3:
输入:
nums
=
[5,1,5,2,5,3,5,4]
\texttt{nums = [5,1,5,2,5,3,5,4]}
nums = [5,1,5,2,5,3,5,4]
输出:
5
\texttt{5}
5
这道题的求解关键在于数组 nums \textit{nums} nums 的性质。
数组 nums \textit{nums} nums 的长度是 2 n 2n 2n,即有 2 n 2n 2n 个元素,其中有 n + 1 n + 1 n+1 个不同的元素且恰好有一个元素重复了 n n n 次,说明除了这个重复 n n n 次的元素以外,还有其余的 n n n 个元素,其余的 n n n 个元素都不等于重复 n n n 次的元素且各不相同,因此其余的 n n n 个元素各出现一次。
由于数组 nums \textit{nums} nums 的其余的 n n n 个元素各出现一次,因此重复了 n n n 次的元素是唯一在数组 nums \textit{nums} nums 中出现超过一次的元素,只要在数组中寻找出现超过一次的元素,即为重复 n n n 次的元素。
判断一个元素是否重复可以使用哈希集合。遍历数组 nums \textit{nums} nums,对于每个元素,如果该元素不在哈希集合中则将其加入哈希集合,如果该元素已经在哈希集合中则为重复元素,将其返回即可。
class Solution {
public int repeatedNTimes(int[] nums) {
int repeat = -1;
Set<Integer> set = new HashSet<Integer>();
for (int num : nums) {
if (!set.add(num)) {
repeat = num;
break;
}
}
return repeat;
}
}
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums \textit{nums} nums 的长度的一半。需要遍历数组 nums \textit{nums} nums 寻找重复元素,最多需要遍历 n + 1 n + 1 n+1 个不同元素。
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums \textit{nums} nums 的长度的一半。需要使用哈希表存储数组 nums \textit{nums} nums 中的元素,最多需要存储 n + 1 n + 1 n+1 个元素。
考虑重复 n n n 次的元素在数组 nums \textit{nums} nums 中所在下标的距离,则一定存在两个相等的元素的下标距离不超过 3 3 3,因此对于任意 1 ≤ i < 2 n 1 \le i < 2n 1≤i<2n,遍历所有满足 max ( 0 , i − 3 ) ≤ j < i \max(0, i - 3) \le j < i max(0,i−3)≤j<i 的 j j j,如果找到 nums [ i ] = nums [ j ] \textit{nums}[i] = \textit{nums}[j] nums[i]=nums[j],则 nums [ i ] \textit{nums}[i] nums[i] 即为重复 n n n 次的元素。
为了寻找重复 n n n 次的元素,必须遍历下标距离为 1 1 1 到 3 3 3 的每一对元素寻找相等元素,而不能只遍历下标距离为 1 1 1 或 2 2 2 的每一对元素寻找相等元素。当 n = 2 n = 2 n=2 时,如果 nums = [ 1 , 2 , 3 , 1 ] \textit{nums} = [1, 2, 3, 1] nums=[1,2,3,1],则重复元素出现了 2 2 2 次且下标距离为 3 3 3,如果只考虑下标距离为 1 1 1 或 2 2 2 的每一对元素则无法找到重复元素。
数组 nums \textit{nums} nums 一定存在两个相等的元素的下标距离不超过 3 3 3。该结论证明如下。
使用反证法。假设数组 nums \textit{nums} nums 的任意两个相等的元素的下标距离都超过 3 3 3,则下标距离至少为 4 4 4。用 start \textit{start} start 和 end \textit{end} end 分别表示重复元素所在的最小下标和最大下标,则有 end − start ≥ 4 ( n − 1 ) \textit{end} - \textit{start} \ge 4(n - 1) end−start≥4(n−1)。
由于数组 nums \textit{nums} nums 的长度是 2 n 2n 2n,下标范围是 [ 0 , 2 n − 1 ] [0, 2n - 1] [0,2n−1],因此有 end − start ≤ 2 n − 1 \textit{end} - \textit{start} \le 2n - 1 end−start≤2n−1。
为了同时满足 end − start ≥ 4 ( n − 1 ) \textit{end} - \textit{start} \ge 4(n - 1) end−start≥4(n−1) 和 end − start ≤ 2 n − 1 \textit{end} - \textit{start} \le 2n - 1 end−start≤2n−1,必须有 4 ( n − 1 ) ≤ 2 n − 1 4(n - 1) \le 2n - 1 4(n−1)≤2n−1,整理得 n ≤ 3 2 n \le \dfrac{3}{2} n≤23。
由于题目要求 n ≥ 2 n \ge 2 n≥2,因此 n ≤ 3 2 n \le \dfrac{3}{2} n≤23 不符合题目要求,数组 nums \textit{nums} nums 的任意两个相等的元素的下标距离都超过 3 3 3 的假设不成立。
因此数组 nums \textit{nums} nums 一定存在两个相等的元素的下标距离不超过 3 3 3。
class Solution {
public int repeatedNTimes(int[] nums) {
int repeat = -1;
boolean flag = false;
int length = nums.length;
for (int i = 1; i < length && !flag; i++) {
for (int j = Math.max(0, i - 3); j < i; j++) {
if (nums[i] == nums[j]) {
repeat = nums[i];
flag = true;
break;
}
}
}
return repeat;
}
}
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums \textit{nums} nums 的长度的一半。需要遍历数组 nums \textit{nums} nums 一次,对于每个元素,最多需要判断该元素的前面 3 3 3 个元素是否与该元素相等,因此总时间复杂度是 O ( 3 × 2 n ) = O ( 6 n ) = O ( n ) O(3 \times 2n) = O(6n) = O(n) O(3×2n)=O(6n)=O(n)。
空间复杂度: O ( 1 ) O(1) O(1)。