在一维坐标轴上有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)
/*
*求系列区间中重合最长的两个区间SegmentOverlap
*@author arhaiyun
*Date:2013-09-29
*
*/
class Segment {
int start;
int end;
Segment(int start, int end)
{
this.start = start;
this.end = end;
}
}
public class SegmentOverlap {
private SegmentOverlap() {}
/**
* 快速排序:对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 <= right && compare(set[left],set[start])<=0)
{
left++;
}
while(left <= right && 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);
}
public static void main(String[] args)
{
//....
}
}