算法之二分本质


以下内容来自SidneySun的ACM公开课

二分引入

二分法,我们大部分是从以下类似的算法题认识到的。

# 从有序int数组 li 中找出数字7的索引
li = [1, 3, 4, 5, 7, 8]

这里的二分,是在一种有序数组中找到某一个特定元素的算法:每次将空间分成两份,然后选择其中的一份继续求解。这样求解空间会不断的减半,直到找到特定的元素

二分的本质

那二分的本质是什么呢,单调有序吗?并不是的。
下面一道算法题,可以思考一下:

给定一个长度为n的数组,左边全是奇数,右边全是偶数,试求最后一个奇数的索引(保证第一个数字一定是奇数)
例如数组 li = [3, 5, 1, 7, 11, 9, 2, 4, 10, 6, 8]
最后一个奇数的索引为奇数9所在的索引5

上面的算法题,是否可以用二分呢?

先取两个索引,left = 0, right = len(li) = 10,二分取中间值 mid = (left + right) // 2,得到 mid = 5, li[5] 为 9,奇数,由此判断,索引5前面的数字肯定都是奇数,将 left 赋值为 5,再取中间值 mid = (5 + 10) // 2 = 7,索引 7 的值为 4,偶数,由此判断,索引7后面的数字全为偶数,将right重新赋值为 7 ,以此类推,我们最后得到 left = 5, right = 6,最后一个奇数的索引值即为5

所以,我们可以发现,二分的本质,是取到一个分界点,可以确定该分界点的某侧有一个共有特性,然后可以根据这个分界点,跳过某些数据的处理,达到迅速便捷的效果
二分中会存在一个整数m,使得

  • 当 i ≤ m 时,f(i)均为真(假)
  • 或当 i ≥ m 时,f(i)均为假(真)

我们可以根据这个性质找到分界点 m

例题

Letcode 455.分发饼干

LetCode链接
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。

对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
示例1

输入: g = [1,2,3], s = [1,1]
输出: 1
解释:
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。

这道题是否有二分性质呢?可以思考一下,如果我能喂饱4个孩子,那我是否能喂饱3个孩子呢?或者,如果我连3个孩子都喂不饱,那我有可能去喂饱4个孩子吗?
相当于,当 i = 4 时,f(i)判断为真,那小于等于 4 的数字都判定为真;当 j = 3 时,f(i)判断为假,那大于等于3的数字也都判断为假
这样,我们就可以将问题转换为是否存在一个方案能喂饱 x 个孩子

采用二分法,得到 mid ,判断能否用最大的 mid 个饼干喂饱饭量最小的 mid 个孩子;若可以喂饱,继续获取更大的 mid 值进行判断能否喂饱。以此类推,我们能得到一个分界点值,即是我们要找的值

Letcode 209.长度最小的子数组

LetCode链接
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
示例

输入:s = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

同样可以思考,这道题是否有二分性质呢。若我们能找到一个长度为 3 的子数组,其和大于 7,那肯定能找到长度为 4 的子数组的和大于 7;同理,若所有长度为4的子数组的和都不大于7,那我们知道长度小于 4 的所有子数组的和,都不可能大于7了。
所以,这道题,同样是可以用二分法来解决的。PS:这里需要用到前缀和,计算出列表所有前缀和,能更轻松的计算每个子数组的和。

li = [2, 3, 5, 1, 4, 9]
所有前缀和:
s0 = li[0] = 2
s1 = s0 + li[1] = 2 + 3 = 5
s2 = s1 + li[2] = 5 + 5 = 10
s3 = s2 + li[3] = 10 + 1 = 11
.
.
.
若要求索引1到3的子数组[3, 5, 1],只需要用 s3 - s0 = 11 - 2 = 9即可

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值