【二分查找】LeetCode:2354.优质数对的数目

简介: 【二分查找】LeetCode:2354.优质数对的数目

作者推荐

贪心算法LeetCode2071:你可以安排的最多任务数目

本文涉及的基础知识点

二分查找算法合集

题目

给你一个下标从 0 开始的正整数数组 nums 和一个正整数 k 。

如果满足下述条件,则数对 (num1, num2) 是 优质数对 :

num1 和 num2 都 在数组 nums 中存在。

num1 OR num2 和 num1 AND num2 的二进制表示中值为 1 的位数之和大于等于 k ,其中 OR 是按位 或 操作,而 AND 是按位 与 操作。

返回 不同 优质数对的数目。

如果 a != c 或者 b != d ,则认为 (a, b) 和 (c, d) 是不同的两个数对。例如,(1, 2) 和 (2, 1) 不同。

注意:如果 num1 在数组中至少出现 一次 ,则满足 num1 == num2 的数对 (num1, num2) 也可以是优质数对。

示例 1:

输入:nums = [1,2,3,1], k = 3

输出:5

解释:有如下几个优质数对:

  • (3, 3):(3 AND 3) 和 (3 OR 3) 的二进制表示都等于 (11) 。值为 1 的位数和等于 2 + 2 = 4 ,大于等于 k = 3 。
  • (2, 3) 和 (3, 2): (2 AND 3) 的二进制表示等于 (10) ,(2 OR 3) 的二进制表示等于 (11) 。值为 1 的位数和等于 1 + 2 = 3 。
  • (1, 3) 和 (3, 1): (1 AND 3) 的二进制表示等于 (01) ,(1 OR 3) 的二进制表示等于 (11) 。值为 1 的位数和等于 1 + 2 = 3 。
    所以优质数对的数目是 5 。
    示例 2:
    输入:nums = [5,1,1], k = 10
    输出:0
    解释:该数组中不存在优质数对。
    参数范围
    1 <= nums.length <= 105
    1 <= nums[i] <= 109
    1 <= k <= 60

分析

时间复杂度

O(nlogn),枚举数对的第二个数,时间复杂度O(n);二分查找第一个数的数量,O(logn)。

原理

如果有重复的数,删除。假定存在b1等于b2,那么任意数对(a,b1)是优质数对,则(a,b2)也是优质数对,故可以删除b1。

c=a&b d = c|b 。c和d 二进制1的数量之和,就是a和b 二进制1的数量之和

去重后,对于任意b, (x,b)都不会重复,因为x不会相等。setNum中的任意数,都可以是x,包括自己。

我们只关心a和b中1的数量,不关心a和b的值。所以枚举vBitCount。

代码

核心代码

//通过 x &= (x-1)实现
int bitcount(unsigned x) {
int countx = 0;
while (x) {
countx++;
x &= (x - 1);
}
return countx;
}
int bitcount(unsigned long long x) {
int countx = 0;
while (x) {
countx++;
x &= (x - 1);
}
return countx;
}
class Solution {
public:
  long long countExcellentPairs(vector<int>& nums, int k) {
    std::unordered_set<int> setNum(nums.begin(), nums.end());
    vector<int> vBitCount;
    for (const auto& n : setNum)
    {
      vBitCount.emplace_back(bitcount((unsigned long long)n));
    }
    sort(vBitCount.begin(), vBitCount.end());
    long long llRet = 0;
    for (const auto& n : vBitCount)
    {
      llRet += vBitCount.end() - std::lower_bound(vBitCount.begin(), vBitCount.end(), k - n);
    }
    return llRet;
  }
};

测试用例

template
void Assert(const vector& v1, const vector& v2)
{
if (v1.size() != v2.size())
{
assert(false);
return;
}
for (int i = 0; i < v1.size(); i++)
{
assert(v1[i] == v2[i]);
}
}
template
void Assert(const T& t1, const T& t2)
{
assert(t1 == t2);
}
int main()
{
vector nums;
int k;
long long res;
{
Solution slu;
nums = { 1, 2, 3, 1 }, k = 3;
auto res = slu.countExcellentPairs(nums, k);
Assert(5LL, res);
}
{
Solution slu;
nums = { 5,1,1 }, k = 10;
auto res = slu.countExcellentPairs(nums, k);
Assert(0LL, res);
}
//CConsole::Out(res);
}

2023年3月版

//通过 x &= (x-1)实现
int bitcount(unsigned x) {
int countx = 0;
while (x) {
countx++;
x &= (x - 1);
}
return countx;
}
class Solution {
public:
long long countExcellentPairs(vector& nums, int k) {
std::sort(nums.begin(), nums.end());
nums.erase(std::unique(nums.begin(), nums.end()), nums.end());
std::vector vOneBitNums;
for (const auto& n : nums)
{
vOneBitNums.push_back(bitcount(n));
}
std::sort(vOneBitNums.begin(), vOneBitNums.end());
int iSameNum = vOneBitNums.end() - std::lower_bound(vOneBitNums.begin(), vOneBitNums.end(), (k + 1) / 2);
long long iNotSameNum = 0;
for (int i = 0; i < vOneBitNums.size(); i++)
{
auto itEnd = vOneBitNums.begin() + i;
iNotSameNum += itEnd - std::lower_bound(vOneBitNums.begin(), itEnd, k - vOneBitNums[i]);
}
return iSameNum + iNotSameNum * 2;
}
};


。也就是我们常说的专业的人做专业的事。 |

|如果程序是一条龙,那算法就是他的是睛|

测试环境

操作系统:win7 开发环境: VS2019 C++17

或者 操作系统:win10 开发环境:

VS2022 C++17

相关文章
|
2月前
【LeetCode 01】二分查找总结
【LeetCode 01】二分查找总结
17 0
|
4月前
|
Python
【Leetcode刷题Python】704. 二分查找
解决LeetCode "二分查找" 问题的Python实现代码。
20 0
|
4月前
|
算法 索引 Python
【Leetcode刷题Python】34. 在排序数组中查找元素的第一个和最后一个位置(二分查找)
解决LeetCode "在排序数组中查找元素的第一个和最后一个位置" 问题的方法。第一种方法是使用两次二分查找,首先找到目标值的最左边界,然后找到最右边界。第二种方法是利用Python的list.index()方法,先正序找到起始位置,再逆序找到结束位置,并给出了两种方法的Python实现代码。
64 0
|
6月前
|
索引
【LeetCode刷题】二分查找:山脉数组的峰顶索引、寻找峰值
【LeetCode刷题】二分查找:山脉数组的峰顶索引、寻找峰值
|
5月前
|
存储 算法
经典的滑动窗口的题目 力扣 2799. 统计完全子数组的数目(面试题)
经典的滑动窗口的题目 力扣 2799. 统计完全子数组的数目(面试题)
|
5月前
2670.找出不同元素数目差数组-力扣(LeetCode)
2670.找出不同元素数目差数组-力扣(LeetCode)
36 0
|
6月前
【LeetCode刷题】二分查找:寻找旋转排序数组中的最小值、点名
【LeetCode刷题】二分查找:寻找旋转排序数组中的最小值、点名
|
3月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
4月前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
61 6
|
4月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
124 2