题目描述
给你一个整数数组 arr,请你检查是否存在两个整数 N 和 M,满足 N 是 M 的两倍(即,N = 2 * M)。
更正式地,检查是否存在两个下标 i 和 j 满足:
- i != j
- 0 <= i, j < arr.length
- arr[i] == 2 * arr[j]
示例1:
输入: arr = [10,2,5,3]
输出: true
解释: N = 10 是 M = 5 的两倍,即 10 = 2 * 5 。
示例2:
输入: arr = [7,1,14,11]
输出: true
解释: N = 14 是 M = 7 的两倍,即 14 = 2 * 7 。
示例3:
输入: arr = [3,1,7,11]
输出: false
解释: 在该情况下不存在 N 和 M 满足 N = 2 * M 。
提示
- 2 < = a r r . l e n g t h < = 500 2 <= arr.length <= 500 2<=arr.length<=500
- − 1 0 3 < = a r r [ i ] < = 1 0 3 -10^3 <= arr[i] <= 10^3 −103<=arr[i]<=103
方法一:二分查找
解题思路
遍历数组中的每个整数,同时利用二分查找来判断该整数的两倍数是否存在于这个数组中。
代码
class Solution {
public:
bool checkIfExist(vector<int>& arr) {
sort(arr.begin(), arr.end());
int n = arr.size();
for(int i = 0; i < n; i++)
{
int x = arr[i] * 2;
int l = 0, r = n - 1, mid;
while(l <= r)
{
mid = (l + r) >> 1;
if(arr[mid] == x) break;
else if(arr[mid] < x) l = mid + 1;
else r = mid - 1;
}
// mid != i 以防查找 0 的两倍,还是本身
if(l <= r && mid != i) return true;
}
return false;
}
};
复杂度分析
- 时间复杂度:O( n l o g n nlogn nlogn)。
- 空间复杂度:O(1)。
方法二:排序 + 双指针
解题思路
先对所有数字进行排序。然后维护两个指针:指针 pp 遍历数字 xx,指针 qq 寻找 2x。
- 对于 x>0 的情况,指针只需要一直前进。若 q 在前进过程中找到一个比 2x 大的数字,那么 2x 必然不存在。在 p 前进的过程中 p 所指向的 x 会不断递增,2x 也会不断递增,因此指针 q 不需要后退。
- 对于 x<0 的情况,指针只需要一直后退。若 q 在后退过程中找到一个比 2x 小的数字,那么 2x 必然不存在。在 p 后退的过程中 p 所指向的 x 会不断递减,2x 也会不断递减,因此指针 q 不需要前进。
代码
class Solution {
public:
bool checkIfExist(vector<int>& arr) {
sort(arr.begin(), arr.end());
for(auto i = arr.begin(), j = arr.begin(); i != arr.end(); i++)
{
while(j != arr.end() && *i * 2 > *j)
j++;
if(j != arr.end() && i != j && *i * 2 == *j)
return true;
}
for(auto i = arr.rbegin(), j = arr.rbegin(); i != arr.rend(); i++)
{
while(j != arr.rend() && *i * 2 < *j)
j++;
if(j != arr.rend() && i != j && *i * 2 == *j)
return true;
}
return false;
}
};
复杂度分析
- 时间复杂度:
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
排序的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),两次指针遍历的过程时间复杂度为 O ( n ) O(n) O(n),综合起来,复杂度为 O ( n l o g n ) O(nlogn) O(nlogn) 。 - 空间复杂度:
O
(
n
)
O(n)
O(n)。
sort 函数空间复杂度为 O ( n ) O(n) O(n),双指针遍历的空间复杂度为 O ( 1 ) O(1) O(1),综合起来,复杂度为 O ( n ) O(n) O(n)。
方法三:哈希表
解题思路
先将所有数字存入哈希表,再遍历所有的数字 x,判断 2x 是否在哈希表中。注意数字 0 需要特殊考虑。可以通过计数来解决数字 0 的问题:若
x
≠
0
x\neq0
x=0,则 2x 只需要出现一次,否则需要出现两次。
由于数字范围是 [-1000,1000],2x 的范围为 [-2000,2000],我们只需要创建一个长度为 4001 的数组 cnt。为了解决下标为负时越界的问题,我们不直接使用数组 cnt,而是设置一个指向 cnt[2000] 的指针
h
a
s
h
_
s
e
t
hash\_set
hash_set 作为哈希表的抽象。这样,取
h
a
s
h
_
s
e
t
[
i
]
hash\_set[i]
hash_set[i] 时会实际取用
c
n
t
[
i
+
2000
]
cnt[i + 2000]
cnt[i+2000],避免了下标越界。
代码
class Solution {
public:
bool checkIfExist(vector<int>& arr) {
int cnt[4001] = {0};
int *hash_set = cnt + 2000;
for(int x: arr)
hash_set[x]++;
for(int x: arr)
{
if(x != 0 && hash_set[2 * x] >= 1)
return true;
else if(x == 0 && hash_set[2 * x] >= 2)
return true;
}
return false;
}
};
复杂度分析
- 时间复杂度:O(n)。哈希表的查询时间复杂度为 O(1),查询次数为 O(n),综合起来,时间复杂度为 O(n) 。
- 空间复杂度:O(n)。哈希表最多需要存储 n 个元素。