区间重合判断问题
问题描述:给定一个区间[x,y]和N个无序的目标区间[x1,y1][x2,y2]…[xn,yn],判断源区间[x,y]是不是在目标区间内(即判断[x,y]属于目标区间的并集是否成立)。详细题目:《编程之美》章节2.19
对于这个问题,我想出了两个解决方案(基本上书上都比我想的多,所以如果想看更权威的解法,还是去看《编程之美》更好)。在这之前,我们先对目标区间进行一下处理
第一步:淘汰掉目标区间中最大值小于源区间最小值的区间
第二步:淘汰掉目标区间中最小值大于源区间最大值的区间
经过上面两步,可以缩小源区间和目标区间的比较范围。然后我们可以开始判断源区间是否包含在目标区间内了。
方案一:
对于源区间的每一个元素,判断其是否在目标区间内。如果源区间中存在某个元素不属于目标区间集合,则返回false;反之,返回true
方案二:
如果剩下的目标区间段能够组合成一个连续的区间,并且组成的连续区间的最大值大于源区间最大值, 连续区间最小值小于源区间最小值, 那源区间必然包含在目标区间中。如果不能连续,由于第一步和第二步对目标区间段进行了筛选,所以不连续一定能证明目标区间不包含源区间
方案二又给出了一个问题,怎样判断若干个区间段是否是一个连续区间?
这个问题我的解决方案是:
(1)将所有目标区间段按最小值从小到大排序
(2)若最小区间段的最大值大于等于次小区间段的最小值,则合并两个区间段,然后循环执行(2)。否则,目标区间段不连续
针对上述两种方案,选用哪种方案解决的方案如下:
如果源区间元素个数较少,而目标区间段比较多,则选择方案一较好
如果源区间元素个数较多,而目标区间段比较少,则选择方案二较好
方案一的代码实现感觉没什么好写的,方案二的代码实现如下:
#include <iostream>
#include <cstdio>
struct Interval
{
int min_val;
int max_val;
Interval *next;
};
Interval *head = new Interval; //指向目标区间的第一个有效的区间段
Interval *source = new Interval; //指向源区间
void InsertToChain(Interval *interval)
{
if(head == NULL)
{
head = interval;
return ;
}
if(interval->min_val < head->min_val)
{
interval->next = head;
head = interval;
return ;
}
Interval *p = head;
while(p->next != NULL)
{
if(interval->min_val < p->next->min_val)
{
interval->next = p->next;
p->next = interval;
return;
}
p = p->next;
}
p->next = interval;
}
void FirstTwoPath()
{
head = NULL;
FILE *fp = fopen("destInterval.dat","rb");
int min = 0,max = 0;
while(1)
{
int len = fscanf(fp,"%d %d",&min,&max);//此处的文件是我用来保存目标区间段的,相信根据读取格式大家就知道该文件的内容格式是怎样的了,就不展示了
if(len < 2) break;
if(min > source->max_val || max < source->min_val) continue;
Interval *destInterval = new Interval;
destInterval->next = NULL;
destInterval->min_val = min;
destInterval->max_val = max;
InsertToChain(destInterval);
}
fclose(fp);
}
bool JudgeContinuity()
{
if(head == NULL) return false;
Interval tempInterval;
tempInterval.next = NULL;
tempInterval.min_val = head->min_val;
tempInterval.max_val = head->max_val;
Interval *p = head;
while(p->next != NULL)
{
Interval *p_next = p->next;
if(tempInterval.max_val < p_next->min_val) return false;
else
{
tempInterval.max_val = (p_next->max_val > tempInterval.max_val)?p_next->max_val:tempInterval.max_val;
}
p = p->next;
}
if(tempInterval.max_val >= source->max_val && tempInterval.min_val <= source->min_val) return true;
else return false;
}
void DeleteChain()
{
Interval *p = head;
while(p != NULL)
{
Interval *p_next = p->next;
delete p;
p = p_next;
}
delete source;
}
int main()
{
std::cout << "输入源区间:";
std::cin >> source->min_val >> source->max_val;
FirstTwoPath(); //执行前两步,用以淘汰掉目标区间中没用的区间段,同时将剩下的区间段进行了排序
bool res = JudgeContinuity(); //判断剩下的目标区间段的连续性
DeleteChain(); //释放动态分配的内存
if(res == true) std::cout << "包含" <<std::endl;
else std::cout << "不包含" <<std::endl;
return 0;
}
好了,把自己的想法写完了,我得去看作者的解决方案了, 晚安~