【座右铭】1. 想要成为行家,就必须尝试解决大量的问题;
2. 解决大量问题并不代表能解决所有问题,而是表示解决下一个问题的几率变大了
1. 在一维坐标轴上有n个区间段,求重合区间最长的两个区间段。【问题来源于v_JULY_v的博客:http://blog.csdn.net/v_july_v/article/details/6803368】
第一部分:思路
1. 对N个区间排序,排序的规则为:先按区间的开始点从小到大排,若开始点相同,则再按结束点排
例如:对于区间[1,3],[2,3],[1,4]排序后的结果是:[1,3],[1,4],[2,3]
2. 采用分而治之的策略,将N个区间分为2个N/2的子问题,则N个区间的重合区间最长的2个区间段,比是以下3种中的一个:
1)是前N/2个区间中重合区间最长的
2)是后N/2个区间中重合区间最长的
3)是前N/2个区间中结束点最大的区间,与后N/2个区间中重合最长的区间
因此有递归式:T(n) = 2*T(n/2) + O(n)
第二部分:Java代码,不考虑异常情况
class Segment {
int start;
int end;
Segment(int start, int end)
{
this.start = start;
this.end = end;
}
}
public class Operator {
private Operator() {}
/**
* 快速排序:对N个区间进行排序,排序的规则为:先按区间的开始点从小到大排,
* 若开始点相同,则再按结束点排
*/
static void quicksort(Segment[] set, int left, int right)
{
if(left<right)
{
int mid = partition(set, left, right);
quicksort(set, left, mid-1);
quicksort(set, mid+1, right);
}
}
static int partition(Segment[] set, int left, int right)
{
int start = left, end = right;
left++;
Segment tmp = null;
while(left<=right)
{
while(left<=end&&compare(set[left],set[start])<=0)
{
left++;
}
while(compare(set[right],set[start])>0)
{
right--;
}
if(left<right)
{
tmp = set[left];
set[left] = set[right];
set[right] = tmp;
}
}
tmp = set[right];
set[right] = set[start];
set[start] = tmp;
return right;
}
static int compare(Segment s1, Segment s2)
{
if(s1.start<s2.start||(s1.start==s2.start&&s1.end<s2.end))
{
return -1;
}else if(s1.start==s2.start&&s1.end==s2.end)
{
return 0;
}else
{
return 1;
}
}
/**
* 递归求解
*/
static Segment[] overlap(Segment[] set, int left, int right)
{
Segment[] ret = null;
if(left<right)
{
ret = new Segment[2];
//往前找结束点最大的
int mid = (left + right) >> 1;
ret[0] = set[mid];
for(int i=mid-1;i>=left;i--)
{
if(set[i].end>ret[0].end)
{
ret[0] = set[i];
}
}
//往后找出重合区间最大的
int maxlen = -1, curlen = -1;
ret[1] = set[mid+1];
maxlen = getLen(ret[0],ret[1]);
for(int j=mid+2;j<=right;j++)
{
curlen = getLen(ret[0],set[j]);
if(curlen==0)
{
break;
}
if(curlen>maxlen)
{
maxlen = curlen;
ret[1] = set[j];
}
}
//前N/2个区间
Segment[] leftmax = overlap(set, left, mid);
if(leftmax!=null&&(curlen=getLen(leftmax[0],leftmax[1]))>maxlen)
{
maxlen = curlen;
ret = leftmax;
}
//后N/2个区间
Segment[] rightmax = overlap(set, mid+1, right);
if(rightmax!=null&&(curlen=getLen(rightmax[0], rightmax[1]))>maxlen)
{
maxlen = curlen;
ret = rightmax;
}
}
return ret;
}
/**
* 返回2个区间重合的长度,其中第一个区间的开始点不晚于第二个区间的开始点
*/
static int getLen(Segment s1, Segment s2)
{
int x = s1.start >= s2.start ? s1.start : s2.start;
int y = s1.end <= s2.end ? s1.end : s2.end;
int len = y - x;
return len>0? len:0;
}
/**
* 对外接口,返回重合区间最长的2个区间
* @param set
* 区间的集合
* @param len
* 集合的长度
* @return
* 重合区间最长的2个区间
*/
public static Segment[] find(Segment[] set, int len)
{
quicksort(set,0,len-1);
return overlap(set,0,len-1);
}
}
第三部分:测试用例
[1,2],[5,6],[3,5]: [3,5],[5,6]
[1,2],[4,6],[1,4]: [1,2],[1,4]
[1,6],[2,4],[3,6]: [1,6],[3,6]
[1,3],[4,8],[6,8],[2,6],[5,10]: [4,8],[5,10]