区间重合判断问题

区间重合判断问题

问题描述:给定一个区间[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;
}

好了,把自己的想法写完了,我得去看作者的解决方案了, 晚安~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值