13.5: 最大线段重合问题(堆实现),敏感度
13.5: 最大线段重合问题(堆实现),敏感度
给定很多线段,每个线段都有左右两个位置 [ start, end ],表示线段开始位置和结束位置,左右都是闭区间,
规定:
1、线段的开始和结束位置一定都是整数值;
2、线段重合区域的长度必须 >= 1,一个点不算重合区域;
线段 a [1, 3]
b [3, 6] ,不算重合区域,只重合了一个点3。
要求返回线段最多重合区域中,包含了几条线段。
比如:
a [ 1 , 8 ]
b [ 2 , 6 ]
c [ 3, 10 ] 数轴上画图,返回线段最多重合区域中,包含了几条线段。
暴力解法:
每个线段都有开始和结束位置,
找所有的线段中开始最小的位置(min开)
找所有的线段中结束最大的位置(max结),此时所有线段都在[ min开, max结 ]这个线段上,
看看有多少线段是包含min开 + 1.5, 包含min开 + 2.5,包含min开 + 3.5 ,包含min开 + 4.5......
(不要写成整数,写成整数会出现只重合一个点的问题),
找其中最大值。
时间复杂度O( (max - min) * N)
1 public static int maxCover1(int[][] lines) { 2 3 int min = Integer.MAX_VALUE; 4 int max = Integer.MIN_VALUE; 5 6 for (int i = 0; i < lines.length; i++) { 7 min = Math.min(min, lines[i][0]); //求出每条线段的左端最小值 8 max = Math.max(max, lines[i][1]); //求出每条线段的右端最大值 9 } 10 11 int cover = 0; 12 for (double p = min + 0.5; p < max; p += 1) { //从p位置开始直到max,0.5 1.5 2.5 ..... 99.5的距离去探测 13 int cur = 0; 14 for (int i = 0; i < lines.length; i++) { //对于每条线段,p点在里面,计数器++ 15 if (lines[i][0] < p && lines[i][1] > p) { 16 cur++; 17 } 18 } 19 cover = Math.max(cover, cur); //内循环一遍结束后,得到:p点在多少条线段中。外循环结束,得到包含p最多的条数。 20 } 21 return cover; 22 }
用堆实现的流程:
所有线段,先根据每个线段的开始位置排序,从小到大,
准备小根堆,目前为空,依次考察每一个线段,按下方思路操作。
线段[ 1, 7 ] 1、把堆中 <= 1的数弹出 2、把结尾7放入小根堆 3、现在小根堆中有[ 7 ],此时有1个数
多解释一下“此时有1个数”:
表示重合区域必须以我这个1为左边界的话,往右延伸了多少条线段。
线段[ 2, 3 ] 1、把堆中 <= 2的数弹出 2、把结尾3放入小根堆 3、现在小根堆中有[ 3 7 ],此时有2个数
多解释一下:
为什么先检查一下小根堆中有多少是小于2的,为什么检查?
如果结尾小于等于2,这个线段[ 2, 2 ]穿不过2,就不是线段了。
把4放进去,堆中的数为2.说明已2为左边界的线段有2条。
线段[ 4, 6 ] 1、把堆中 <= 4的数弹出 ,3出 2、把结尾6放入小根堆 3、现在小根堆中有[ 6 7 ],此时有2个数
线段[ 4, 5 ] 1、把堆中 <= 4的数弹出 , 2、把结尾5放入小根堆 3、现在小根堆中有[ 5 6 7 ],此时有3个数
所有线段的答案都求出来,找最大的那个,就是最大线段重合数。
任何一个重合区域的左边界必是某个线段的左边界
考察每一个线段的左边界,假设它是重合区域的左边界,求出从这个左边界开始向右的贯穿数,答案必是那个最大的数。
O(N * logN)
1 public static class Line { 2 public int start; 3 public int end; 4 5 public Line(int s, int e) { 6 start = s; 7 end = e; 8 } 9 }
public static class StartComparator implements Comparator<Line> {
@Override
public int compare(Line o1, Line o2) {
return o1.end - o2.end;
}
}
1 public static int maxCover2(int[][] m) {
2 Line[] lines = new Line[m.length];
3 for (int i = 0; i < m.length; i++) {
4 lines[i] = new Line(m[i][0], m[i][1]);
5 }
6 Arrays.sort(lines, new StartComparator());
7 // 小根堆,每一条线段的结尾数值,使用默认的
8 PriorityQueue<Integer> heap = new PriorityQueue<>();
9 int max = 0;
10 for (int i = 0; i < lines.length; i++) {
11 // lines[i] -> cur 在黑盒中,把<=cur.start 东西都弹出
12 while (!heap.isEmpty() && heap.peek() <= lines[i].start) {
13 heap.poll();
14 }
15 heap.add(lines[i].end);
16 max = Math.max(max, heap.size());
17 }
18 return max;
19 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现