Leetcode——二分

33.搜索旋转排序数组

做法:先利用二分搜索得到未经过旋转的排序数组起始元素在给定数组的索引,我们将这个索引称为分界点。分界点会划分出两个有序区间,我们分别对这两个区间进行二分搜索,从而得到题目的解。

问题来了,如何利用二分搜索得到分界点?
我们直到,原始数组是有序的,而题目给的是经过旋转后的数组,因此旋转点就是题目给定数组的首元素。因此,可以利用给定数组中必然存在一个左区间所有的元素均大于等于旋转点这一性质来判断分界点。
34.在排序数组中查找元素第一个和最后一个位置

做法:利用二分模板分别查找左边界和右边界即可。

35.搜索插入位置

69.x的平方根

方法:二分。
注意事项:1. 越界 2. 精度不够
学到的知识

  • (left + right)/2 = left + (right - left )/2
  • 左移运算符>> 的优先级低于加减乘除的优先级

74.搜索二维矩阵

方法:二分查找

81.搜索旋转排序数组II

步骤如下

  1. 循环,该循环将数组尾端所有等于nums[0]的元素拿出二分搜索范围。
  2. 寻找旋转点
  3. 判断旋转点是否是target
  4. 对左右区间二分,查找是否存在target

162.寻找峰值

方法:二分
二分的本质是二段性,而非单调性
二分的二段行可以进一步划分,不仅仅只有满足01特性(满足/不满足)的二段性可以使用二分,满足1?特性也可以二分。

240.搜索二维矩阵II

方法:

  1. 二分,本题并不保证下一行的第一个数字一定大于本行的最后一个数字
  2. 抽象BST。题目所给的矩阵可以看作是一个以右上角的节点为根节点的二叉树。

278.第一个错误的版本

思路和方法:二分。

274.H指数

这道题目可以使用二分来解决,但我不知道怎么二分

那我就说一下我的做法吧。
根据H指数的定义,实际上是找一个最大的x,使得

  1. i < n - x,citations[i] <= x
  2. i >= n - x,citations[i] >= x

那么当i = n - x时,有citations[i] >= x

根据以上分析,我的解法如下:

  1. n = citations.size();
  2. citations从小到大排序
  3. 从左到右遍历citations,返回第一个n - i,使得citations[i] >= n - i

275.H指数II

题目要求citations 已经按照 升序排列,要求设计并实现对数时间复杂度的算法解决此问题

思路和算法:利用数组的有序性进行二分。
假设存在一个分割点x,其值为citations[x]。分割点右边(包括分割点)的论文数量是n - x,若n - mid满足是H指数,则必然有citations[x] >= n - x。要使得H指数尽可能大,那么mid就必须尽可能地小。下面代码是左右边界的更新方式。

	while(left < right){
        int mid = left + (right - left) / 2;
        if(citations[mid] >= n - mid) right = mid;
        // 说明有n - mid篇文章的引用次数至少是n - mid次,n - mid符合要求
        // 题目中要求H指数尽可能大,所以mid尽可能地小,因此令right = mid
        else left = mid + 1;
    }

287.寻找重复数

这道题目的二分真的是为所未闻。

334.递增的三元组

本题目实际上是最长上升子序列的应用。
若数组的最长上升子序列的长度大于3,返回true

367.有效的完全平方数


410.分割数组的最大值

方法:二分 + 贪心

我的做题历程:最开始想到了二分,但我想的二分是二分数组,后来觉得不合适。看了答案,二分的是子数组的最大和。

言归正传,下面是二分 + 贪心的思路。

思路

在整个整数范围内二分子数组的最大和mid,判断数组中是否能够分割出小于等于m个子数组,使每个子数组的和都小于mid,若不存在,说明mid太小,答案在右区间中,于是令左边界等于mid + 1,若存在,说明答案在左区间中,于是令有边界等于mid。这样一次又一次地缩小查找范围,直到左右边界重合。

如何判断数组中是否能够分割出小于等于m个子数组,使每个子数组的和都小于mid?这就要用到贪心算法了。

贪心算法的具体思路是这样的:遍历整个数组,若当前子数组再加上当前元素的和小于等于mid,就把当前元素加入到当前子数组中,否则另起一个新的子数组。最后统计子数组的数目和m的大小。


436. 寻找右区间

看到这道题目,一个直观的想法是:

  1. 把每个区间的左端点left及其相应的索引idx合在一起形成对组{left,idx},并存储在容器V
  2. v中的元素按照左端点从小到大的顺序排列
  3. 根据题意查找合法的区间左端点

C++中的STL就提供了这样一种数据结构mapmap有如下特点:

  1. 底层实现是红黑树
  2. 内部元素按照键值从小到大的顺序有序排列
  3. 提供了查找函数,如find,lower_bound,upper_bound,这三个函数的时间复杂度均为 O ( log ⁡ n ) O(\log n) O(logn)。其中,find用于查找map中是否存在键值key所对应的元素,若存在,返回其迭代器,否则返回尾后迭代器;lower_bound返回第一个键值大于等于key的元素的迭代器,upper_bound返回第一个键值大于key的元素的迭代器。

475.供暖器

方法:二分 + 双指针,双指针方法用于快速判断房屋能够被供暖器全部覆盖,二分方法用于逼近能被供暖器覆盖的最短半径。

先考虑最简单的一种情况:已知供暖器的覆盖半径r,如何快速判断房屋能够被供暖器全部覆盖?做法如下:

  1. 对房屋位置数组houses和供暖器位置数组heaters进行从小到大的排序
  2. 定义指针i和指针j分别指向housesheaters
  3. 判断houses[i]是否位于heaters[j]的覆盖范围内,即判断houses[i] >= heaters[j] - radius && houses[i] <= heaters[j] + radius,若结果为真,则递增i,否则就递增j
  4. 重复上述步骤,直到结束对housesheaters的遍历

容易得到,供暖器的覆盖半径r一定满足 r > r m i n r > r_{min} r>rmin,其中 r m i n r_{min} rmin是供暖器的最小覆盖半径。如果能够提供供暖器的覆盖半径的区间,并要求从该区间中找到供暖器的最短覆盖半径,很容易会想到二分算法。由于该题目可以提供供暖器的覆盖半径区间,因此可以使用二分算法进行解决。


497. 非重叠矩形中的随机点

思路:二分 + 前缀和

题目要求等概率地寻找所有满足要求的点,因此该题目的关键在于准确计算所有满足要求点的数量。


:为防止越界,求解分界点时尽量使用left + (right - left) / 2。此外,这种写法还适用于迭代器。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值