【二分查找】leetcode 1346. 检查整数及其两倍数是否存在

1346. 检查整数及其两倍数是否存在

题目描述

给你一个整数数组 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 个元素。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值