java 二分搜索获得大于目标数的第一位_遇到「最值问题」还在无脑动态规划?二分法考虑一下呗
目录
- 前言
- 二分法基础及变种结构
- 小试牛刀
- 打怪升级
- 出师试炼
前言
一般来说,遇到「最值问题」通用的方法都是动态规划,而有一类「最值问题」可以用其他方法更加巧妙、简单方便的解决,这类问题的常见问法是「使……最大值尽可能小」。
这类问题也是大厂笔试面试常见题型,2020 年美团笔试题、字节面试题中都出现过。
这类问题有三大特征:
- 求最值(一般是最小值)
- 值的搜索空间是线性的
- 对该值有一个限制条件,并且若 x 满足条件,则 [x, +∞) 都满足条件,反之 (-∞, x] 都不满足条件。
你说的我都懂,可问题是,这是啥意思呢?叉会腰,冷静一下。
为了方便大家理解这类问题到底是个什么玩意儿,本汪在这里列出 leetcode 上的一道题目作为例子:
给定一个非负整数数组和一个整数 m,你需要将这个数组分成 m 个非空的连续子数组。设计一个算法使得这 m 个子数组各自和的最大值最小。
不熟悉二分法的同学初遇此题,看到关键词“分割成 m 份”、“最小”,就想到了动态规划。诚然此题具备了使用动态规划的一切前提条件,也确实可以通过动态规划做出来,但其时间复杂度为 O(n^2 * m) , 空间复杂度为 O(n * m).
如果你看了本汪的这篇文章,学会了用二分法解决这一类问题,那么时间复杂度可以优化到 O(n * logx), 空间复杂度可以优化到 O(1),并且思路非常简洁,代码实现也极其简单。
二分法基础及变种结构
在讲具体的方法之前,本汪先和大家一起回顾下二分法,熟悉二分法的同学可以直接跳过这部分。
最早接触二分思想的地方应该是在二分搜索中。(本质上类似快排中的 partition 思想)
二分法是在有序线性搜索空间内,利用有序性,每次搜索通过排除一半的剩余搜索空间,使 O(n) 级别的线性搜索优化到 O(logn) 级别的方法。
二叉树:我都会二分,不会还有人不会二分法吧?不会就让往西汪那小子教你
二分法的基本代码非常简单,这里就不多提了,下面用类 java 语言给出其针对「最值问题」的变种结构:
// nums 数组是搜索空间,m 是限制条件// check(x, m) 表示在搜索空间 nums 中的点 x,满足了限制条件 mpublic int binary(int[] nums, int m){ 初始化 l, r 为搜索空间的起始点和终点 while(l > 1; //二分,一次搜索可以排除 mid 的左边或者右边(剩余搜索空间的一半) if(check(mid, m)) r = mid; //因为这一类最值问题要找的是最小,mid 满足条件了,(mid, r] 就不是最小的答案了 else l = mid + 1; // mid 不满足条件,根据有序性,[l, mid] 里的数全不满足条件 } return r;}
根据结构我们可以看出,遇到这类问题的时候只需要找到「搜索空间」、「检查条件」,然后套用结构就能轻轻松松地解决问题。
下面就让我们来用二分法解决刚刚提到的问题。
小试牛刀
题目
给定一个非负整数数组和一个整数 m,你需要将这个数组分成 m 个非空的连续子数组。设计一个算法使得这 m 个子数组各自和的最大值最小。
思路
前文提及,解决本题只需要找到「搜索空间」、「检查条件」,剩下的就是闭着眼睛套结构的事儿了。
先找「搜索空间」,因为此类问题的搜索空间都是线性的,所以找到了起始点和终点,也就找到了搜索空间。
看问题描述 “子数组各自和的最大值最小”,也就是说我们搜索的点是 "子数组各自和的最大值",那么搜索空间的起始点是数组 nums 中的最大值,即 min(nums),搜索空间的终点是数组 nums 中的所有元素之和,即 sum(nums)。因此我们找到了搜索空间,为 [min(nums), sum(nums)].
再看「检查条件」。给定搜索空间 nums,和点 x,判断 x 是否满足限制条件 m。
本题的条件是“子数组各自和的最大值”,也就是说 x 和 m 个子数组各自和相比较,都要大于或者等于它们。
「搜索空间」、「检查条件」都找到了,下面闭着眼睛套结构吧~
(瑟瑟发抖.jpg) 好的,这就上代码
代码
class Solution { // 这个方法就是直接套结构 public int splitArray(int[] nums, int m) { long l = 0, r = 0; for(int num: nums) {r += num; l = Math.max(l, num);} // 初始化 l 和 r while(l > 1; if(check(nums, mid, m)) r = mid; else l = mid + 1; } return (int)r; } public boolean check(int[] nums, long a, int m) { // 给定搜索空间中的点 a,看它是否大于等于所有子数组的各自和 int cnt = 1; long sum = 0; for(int n: nums) { sum += n; if(sum > a) { sum = n; cnt ++; if(cnt > m) return false; } } return true; }}
打怪升级
咱们再来看一道题,小伙伴们可以先自己思考,有思路的话自己动手实现下。再看看本汪给出的思路,加深理解。
题目
珂珂喜欢吃香蕉。这里有 N 堆香蕉,第 i 堆中有 piles[i] 根香蕉。警卫已经离开了,将在 H 小时后回来。
珂珂可以决定她吃香蕉的速度 K (单位:根/小时)。每个小时,她将会选择一堆香蕉,从中吃掉 K 根。如果这堆香蕉少于 K 根,她将吃掉这堆的所有香蕉,然后这一小时内不会再吃更多的香蕉。
珂珂喜欢慢慢吃,但仍然想在警卫回来前吃掉所有的香蕉。
返回她可以在 H 小时内吃掉所有香蕉的最小速度 K(K 为整数)。
往西汪:我请你们吃香蕉,可以给我点个赞吗
思路
老规矩,先找「搜索空间」,再找「检查条件」,最后闭着眼睛套结构。
「搜索空间」:H 小时内必须吃完 sum(piles) 根香蕉,所以初始点为 sum(piles) / H,一次最多只能吃一堆,所以终点为 max(piles)。因此,搜索空间为 [sum(piles) / H, max(piles)]。
「检查条件」:“在 H 小时内吃掉所有香蕉”,因此以 K 个 / 小时 的速度吃完香蕉的用时要小于等于 H。
下面就闭着眼睛套结构吧。
代码
class Solution { public int minEatingSpeed(int[] piles, int H) { int l = 1, r = 0; // 这里本汪为了代码的简洁性,在不影响时间复杂度的情况下,直接让初始点为 1 for(int i: piles) r = Math.max(r, i); // 一次最多吃一堆 while(l > 1; if(check(piles, mid, H)) r = mid; else l = mid + 1; } return r; } boolean check(int[] piles, int K, int H){ int t = 0; for(double i: piles){ t += Math.ceil(i / K); // 一堆香蕉共计 i 个,需要 ⌈i / K⌉ 个小时吃完 if(t > H) return false; // 警卫回来了还没吃完 } return true; }}
出师试炼
我非常喜欢看我文章的小伙伴,个个都是人才,说话又好听,脑瓜子又聪明,还很主动的给我文章点赞。我相信砍翻两个小怪之后,你们已经是这类问题的专家了,下面就给几道题目供大伙随便玩玩。
超喜欢往西汪的文章的
关卡 1
你将会获得一系列视频片段,这些片段来自于一项持续时长为 T 秒的体育赛事。这些片段可能有所重叠,也可能长度不一。
视频片段 clips[i] 都用区间进行表示:开始于 clips[i][0]并于 clips[i][1] 结束。我们甚至可以对这些片段自由地再剪辑,例如片段 [0, 7] 可以剪切成 [0, 1] + [1, 3] + [3, 7] 三部分。
我们需要将这些片段进行再剪辑,并将剪辑后的内容拼接成覆盖整个运动过程的片段([0, T])。返回所需片段的最小数目,如果无法完成该任务,则返回 -1 。
关卡 2
牛牛有 n 件带水的衣服,干燥衣服有两种方式。
一、是用烘干机,可以每分钟烤干衣服的 k 滴水。
二、是自然烘干,每分钟衣服会自然烘干 1 滴水。
烘干机比较小,每次只能放进一件衣服。
注意,使用烘干机的时候,其他衣服仍然可以保持自然烘干状态,现在牛牛想知道最少要多少时间可以把衣服全烘干。
关卡 3
你太强了!我已经没有更多的题目给你玩了。你可以凭借这套武功闯荡江湖了!
……
……
……
等等,我说笑呢。你这小身板,要不先进我的其他训练营再练练呗?
啥,你问我的其他训练营在哪里?动动小手,点个关注,新的训练营已经在建设了,嘿嘿。
咳咳,暗示的很明显了
java 二分搜索获得大于目标数的第一位_遇到「最值问题」还在无脑动态规划?二分法考虑一下呗相关推荐
- java 二分搜索获得大于目标数的第一位_程序员常用查找算法(顺序、二分、插值、分块、斐波那契)...
顺序查找 基本思想 属于线性查找和无序查找,从一端开始顺序扫描,直到找到与目标值value相等的元素. 这是最基本的查找方法,也是时间复杂度最高的查找算法. 在数据过多时,这种方法并不适用. 代码实现 ...
- java 二分搜索获得大于目标数的第一位_程序员数据结构算法编程,二分查找搜索算法的原理与应用介绍!...
本文来讲一种搜索算法,即二分搜索算法,通常在面试时也会被问到. 我们先来看一个例子,在图书馆通常是根据查到的编号去找书,可以在书架上按顺序一本本地查找,也可以找到一本书不符合预期时,再跳过一大部分书再 ...
- java 二分搜索获得大于目标数的第一位_Java后端架构师技术图谱,你都了解多少?...
前言 欢迎工作一到五年的Java工程师朋友们加入我们,私信回复[资料]即可获取我们提供免费的Java架构学习资料(里面有高可用.高并发.高性能及分布式.Jvm性能调优.Spring源码, MyBati ...
- 数显之家快讯:「SHIO世硕心语」曾国藩:人没格局,比没钱更可怕!
数显之家快讯:「SHIO世硕心语」曾国藩:人没格局,比没钱更可怕 做人的最高意境是节制,而不是释放. 所谓大格局.高智慧,无非就是一种平静平和的内心状态. 格局越大,越不纠缠 真正有大格局的人,非常明 ...
- 快手(java岗)秋招三面,已拿offer「面经分享」
地址:北京 编辑 添加图片注释,不超过 140 字(可选) 2021.9.7 一面 1. 自我介绍 2. 实习项目.背景.需求介绍 3. InnoDB优点 4. MyISAM索引底层是什么结构 5 ...
- 谷歌OKR工作法|目标管理法|企业团队个人效率提升「吱序APP」
一.OKR发展史 二.什么是OKR? OKR(Objectives and Key Results)目标及关键成果法.是一套用来明确目标和跟踪目标完成情况的目标管理工具和方法. 目标(Objectiv ...
- sql timestep 秒数后6位_被央视批评后仍不悔改?如今带6位助理30个保镖出门!网友:气派...
不知道大家喜不喜欢吴谨言这个女演员,去年她主演的<延禧攻略>大火,里面的角色一改往日清宫剧的傻白甜或者悲情,让人眼前一亮,也让大家一下子记住了这个怼天怼地,敢爱敢恨的小姑娘.小编也是从那会 ...
- ug9.0 java虚拟机安装_UG NX9.0安装方法与安装文件「详细图文教程」
前言:UG9.0及9.0以上的版本是没有32位安装包的,如果你使用的是32位的操作系统,那么你的UG软件最高可以安装到UG8.5版本.UG9.0及其以上的版本,必须安装在64位的操作系统. PS:以后 ...
- 二分查找 —— 有序数组不小于(不大于)某数的第一个(最后一个)元素
1. 不小于某数的第一个元素 def bisearch(l, e, lo, hi):while lo < hi:mi = (lo + hi)//2if e > l[mi]: lo = mi ...
最新文章
- spring boot学习资料以及DEMO项目
- mysql中transaction的实现
- query分页共享,可传参
- HBase的Shell操作
- Matlab dir函数
- Kubernetes的四种用户部署场景
- 大白话说Java泛型:入门、使用、原理
- 新手使用vue-router传参时注意事项
- NIOS生成Nios libaray
- 新年新气象[xgluxv]
- 机器学习笔记(二)线性回归模型实现
- JS开发3D建模软件
- 大数据分析项目生命周期
- X书app数美-sid分析
- 教matlab唱周董的《七里香》
- MATLAB矩阵生成
- 利用英超FPL数据分析球员第一期——曼联半程MVP
- adb命令之pm hide 与 disable
- 03 限制 limit
- 设计黄金法则永不改变
热门文章
- 快速入门 Nginx,这篇就够了!
- 也许,这样理解 HTTPS 更容易!
- 解决原子性问题?你首先需要的是宏观理解
- BAT 招聘岗位 100%都考的知识,你精通了吗?
- hbuilderx代码自动补全_DL时代的代码补全利器,北大出品,效果远超语言模型
- mxnet 查看中间层结果
- 检测跟踪 DeepSOCIAL:基于YOLOv4的人群距离监测 集检测、跟踪以及逆透视映射一体的系统
- Lite-HRNet
- tempfile PermissionError: [Errno 13] Permission denied
- Et.parse(xml) #39gbk#39 codec cant decode byte