题目:求线段最大重合数
给定很多线段,每个线段都有两个数[start,end],表示线段开始位置和结束位置,左右都是闭区间,所有的线段都记录在整数数组 lines ,其中lines[i] = [Xstart, Xend]。
规定:
- 线段开始和结束位置一定是整数
- 线段重合区域的长度必须 >= 1
返回线段最多重合区域中,包含了几条线段
示例1:
输入:lines=[ [3,6], [2,8],[4,7],[7,12] ]
输出:3
解题思路
方案一:暴力解法
思路:
有题目可知,线段的开始位置和结束位置都是整数,且线段长度大于等于1。因此,假设有个线段为[0,1],那么0.5肯定在该线段上。由此,可以通过暴力方式,求解数组中没一个x.5位置所在的线段个数,从而取最大值。
代码实现:
- 先求出数组的左右边界值,左边界为所有线段开始位置的最小值,右边界为所有线段结束位置的最大值。
- 循环计算每一个X.5位置线段重合个数。
public static int maxCoverCount1( int[][] lines ){
//1.计算左右边界
int left = 0;
int right = 0;
for( int i = 0; i < lines.length; i++ ){
left = Math.min( left, lines[ i ][ 0 ] );
right = Math.max( right, lines[ i ][ 1 ] );
}
//2.暴力寻找包含X.5位置线段个数
int max = 0;
for( double p = left + 0.5; p < right; p += 1 ){
int cur = 0;
for( int i = 0; i < lines.length; i++ ){
if( lines[ i ][ 0 ] < p && p < lines[ i ][ 1 ] ){
cur++;
}
}
max = Math.max( max, cur );
}
return max;
}
方案二: 使用小根堆
思路:
给定的lines数组中的线段是无序的,计算线段重合个数时,由于线段无序,就需要同时考虑开始和结束两个位置的大小。
可以通过将数组按照开始位置排序,减少重合判断条件。此时,只需要比较某个线段的结束位置是否小于下一个线段的开始位置。
如过一个线段的结束位置小于另一个线段的开始位置,这两条线段肯定是不重合的。
已给定数组 lines = [ [3,6], [2,8],[4,7],[7,12] ] 为例。
排序后数组为:
lines = [ [2,8], [3,6],[4,7],[7,12] ]
- 小根堆 [ ] --> 执行 [2, 8] --> 小根堆 [8 ] ; count =1
- 执行 [3, 6]–>8大于3 --> 小根堆 [6,8 ] ; count =2
- 执行 [4,7] -->6,8都大于4 -->小根堆 [6,7,8 ] ; count =3
- 执行 [7,12]–> 6,7,8都小于12,小根堆弹出6,7,8 -->小根堆 [12 ]; count =1
代码实现
步骤:
- 将所有的线段按照开始位置进行排序,目的是为了保证任何一个线段尾部dou可以从右侧进来。
- 准备一个小根堆,用来存放线段的尾部。
- 对于每条线段,先将小跟堆中所有小于线段起点的数弹出。
- 将线段的尾部加入小根堆中,此时,小根堆中的数据个数就是重合的线段个数。
- 计算当前小跟堆大小,取最大值。
public int maxCoverCount( int[][] lines ){
if( lines.length == 0 ){
return 0;
}
// 1.排序
Arrays.sort( lines, ( x, y ) -> Integer.compare( x[ 0 ], y[ 0 ] ) );
int max = 0;
// 创建小根堆
PriorityQueue<Integer> heap = new PriorityQueue<>();
for( int i = 0; i < lines.length; i++ ){
// 判断线段是否重合,并计算堆的长度
while( !heap.isEmpty() && heap.peek() <= lines[ i ][ 0 ] ){
heap.poll();
}
heap.add( lines[ i ][ 1 ] );
max = Math.max( max, heap.size() );
}
return max;
}