LeetCode 二分习题

本文探讨了如何利用二分搜索优化算法解决两个问题:计算研究者H指数的高效方法和最小化绝对差值和的策略。通过二分查找模板和排序技巧,分别降低了复杂度,实例解析了H指数的二分查找算法和绝对差值和的优化解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

二分模板

274. H 指数

1818. 绝对差值和


 

二分模板

模板 #1 (left <= right)

  • 用于查找一定存在的元素。
  • 查找条件可以在不与元素的两侧进行比较的情况下确定(或使用它周围的特定元素)。
  • 不需要后处理,因为每一步,都在检查是否找到了元素。如果到达末尾,则知道未找到该元素。

模板 #2 (left < right)

  • 适用于查找峰值之类,峰值一定存在,夹逼出唯一一个结果,只要夹逼出l=r的情况就是结果
  • 查找条件需要访问元素的直接右邻居。
  • 使用元素的右邻居来确定是否满足条件,并决定是向左还是向右。
  • 需要进行后处理。 当你剩下 1 个元素时,循环 / 递归结束。 需要评估剩余元素是否符合条件

 


274. H 指数

给定一位研究者论文被引用次数的数组(被引用次数是非负整数)。编写一个方法,计算出研究者的 h 指数。某人的 h 指数是 20,这表示他已发表的论文中,每篇被引用了至少 20 次的论文总共有 20 篇。

        首先想到的是排序二分法。

        数组排序之后,二分。对于citations[mid]而言,大于等于它的论文计数有size - mid篇。根据题意,符合要求的情况应该是 citations[mid] >= size - mid,也就是说有size - mid篇论文,每篇引用次数至少是size - mid。由此可以构建二分条件:

        当citations[mid]< size - mid,引用次数小于篇数,++l。当citations[mid] > size - mid,这是一种符合的情况,可能会有更有解,保持当前条件,r = mid。否则当citations[mid] == size - mid。完全符合条件,返回 size - mid;

        退出循环后,l指针应该指向符合条件的论文。篇数有size - l篇

int l = 0, r = size;
while(l < r){
    int mid = l + ((r - l) >> 1);
    if( citations[mid] > size - mid)     r = mid;
    else if( citations[mid] < size - mid)l = mid + 1;
    else return size - mid;
}
return size - l;

1818. 绝对差值和

给你两个正整数数组 nums1 和 nums2 ,数组的长度都是 n 。数组 nums1 和 nums2 的 绝对差值和 定义为所有 |nums1[i] - nums2[i]|(0 <= i < n)的 总和(下标从 0 开始)。你可以选用 nums1 中的 任意一个 元素来替换 nums1 中的 至多 一个元素,以 最小化 绝对差值和。在替换数组 nums1 中最多一个元素 之后 ,返回最小绝对差值和。因为答案可能很大,所以需要对 109 + 7 取余 后返回。

        很多人一开始都想到贪心,一次遍历找到最大绝对差值的地方,第二次遍历进行比较替换,最终发现错在[1,28,21][9,21,20]

        再来理一下思路。对于第i个元素有|nums1[i] - nums2[i]|,我们想知道是不是存在这么一个j,使得|nums1[j] - nums2[i] | < |nums1[i] - |nums2[i] |,最终使得绝对值之和最小。这里就和以前做的有几个子数组类的问题差不多了。我们的目标就是,对于每一个元素i,我们寻找一个j,尽可能多的减少差值绝对和,使得下式最大。

| nums1[i] - nums2[j] | - |nums1[j] - nums2[i] |

        j元素的选择可以遍历但是那是O(n)的。可以复制一份nums1,并对其排序,对结果二分找lower_bound, nums2[i]降到O(lg<sub>n</sub>)。因为lower_bound只会找到第一个大于等于查找元素值的位置。可以将其转为int下标id,id减一则为另外一个最接近查找元素的位置。我们从这两个之中在选择最大的。

sort(num1.begin(), num1.end());
for(int i = 0; i < size ; ++i)
{
    diff = abs(nums1[i] - nums2[i]);
    ans =(ans + diff) % x;
    int  l = lower_bound(num1.begin(), num1.end(), nums2[i]) - num1.begin();
    if(l < size) maxn = max(maxn, diff - (num1[l] - nums2[i])   );
    if(l > 0)    maxn = max(maxn, diff - (nums2[i] - num1[l - 1]));
}
return (ans - maxn + x) % x;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值