码迷,mamicode.com
首页 > 其他好文 > 详细

求重叠区间个数,某书某题错例分析

时间:2016-01-06 08:04:35      阅读:514      评论:0      收藏:0      [点我收藏+]

标签:

大意就是我淘钱买了一本题集,觉得书中有些地方作者太随意,例子错得不严谨,一度阻碍阅读。作为消费者不得不拿出来说一说。
本文本着不迷信,实事求是精神。
本文编排如下:
1.引用书中原例
2.主观分析例子有错
3.代码运行验证其错
4.修正例子代码
5.另一个求值代码
现在开始。

 

下面是书中原例引用:

求重叠区间个数
给定多个可能重叠的区间,找出重叠区间的个数。区间定义如下:
class Interval {
    int start; // 起点
    int end; // 止点
    Interval(int a, intb) {
    start=a;
    end=b;
    }
}
举例
输入:[1,5], [10,15], [5,10], [20,30]
输出:3
思路
假如输入的四个区间:[start1,end1], [start2,end2], [start3,end3], [start4,end4], 我们不区分区间点的类型,而是根据区间点的大小排序,得到start1-start2-start3-end1-end2-end3-start4-end4。然后扫描上面结果,当遇到起点时,重叠个数加一,并且记录重叠个数的最大值;否则当遇到止点时,重叠个数减一。
首先,要定义区间的类,实现Comparable接口,含有起点与止点的值和类型,还要重写用于排序的compareTo函数。
class Point implements Comparable<Point> {
    int value; // 数值
    int type;  // 点的类型, 0为起点, 1为止点
    Point(int v, int t) {
        value = v;
        type = t;
    }
    // 还需要实现compareTo函数,以便排序
    public int compareTo(Point p) {
        if(this.value == p.value) {
            return 0;
        } else if (this.value > p.value) {
            return 1;
        } else {
            return -1;
        }
    }
}

int getOverlappingCount(Interval[] A) {
    int max=0, count=1;
    if(A==null || A.length==0) return max;
    Point[] points = new point[A.length*2];
    for(int i=0; i<A.length; i++) { // 转为可排序的点
        points[2*i] = new Point(A[i].start, 0);
        points[2*i+1] = new Point(A[i].end, 1);
    }
    Collections.sort(points); // 排序
    for(int i=0; i<points.length; i++) {
        if(points[i].type==0) {
            count++;  // 起点
            max = Math.max(max, count);
        } else {
            count--;
        }
    }
    return max;
}

作者给出的思路,有两步,首先对区间进行区间点的排序, 以及之后对重叠的判定。


作者给出对重叠的判定是这样:扫描排序后的结果,“当遇到起点时,重叠个数加一,并且记录重叠个数的最大值;否则当遇到止点时,重叠个数减一”。但是怎么想都应该是当遇到起点时,计数加一,表示进入区间;当遇到止点时,计数减一,表示离开区间。计数不为0时,再次遇到另一个起点,表明区间发生了重叠,这时重叠数加一。通过扮演分析,作者给出的判定方法,对于一个有序的每三个地重叠的的区间序列中,只能得到一个3的max值。


此外作者给出的排序方法也有问题,根据作者的思路,他所希望的排序结果是,当边界发生重叠时,起点值一定是排在止点值的前面。对于作者的举例输入“[1,5], [10,15], [5,10], [20,30]”中,如果排序是稳定的话,"[5"一定不会被排在"5]"的前面。所以比较方法中缺少了一个类型优先的次级比较条件。


并且作者为得出他举例的结果3,在getOverlappingCount函数中作弊将count初始值为1。对于区间点排序的结果,第一个点一定是起点类型,即使输入中只有一个区间,或输入的是一些不重叠的区间,作者的代码都返回2。虽然区间发生重叠最少是2个不可能1个区间自己发生重叠,但是作者初始count=1明显不符合它自身的意义。

现在将书中的java代码转换成是c++代码,运行验证。

struct Interval
{
    int start;
    int end;
    Interval(int a, int b)
    {
        start = a;
        end = b;
    }
};

struct Point
{
    int value;
    int type;
    bool operator< (Point& _R)
    {
        return value < _R.value;
    }
};

int getOverlappingCount(std::vector<Interval>& A)
{
    int max = 0;
    int count = 1;
    std::vector<Point> points;
    points.resize(A.size() * 2);
    for(int i = 0; i < A.size(); ++i)
    {
        points[2*i].value = A[i].start;
        points[2*i].type = 0;
        points[2*i+1].value = A[i].end;
        points[2*i+1].type = 1;
    }
    std::sort(points.begin(), points.end());
    for(int i = 0; i < points.size(); ++i)
    {
        printf("%s%d%s, ", points[i].type ? "":"[", points[i].value, points[i].type ? "]":"");
        if(points[i].type == 0)
        {
            count++;
            max = std::max(max, count);
        }
        else
        {
            count--;
        }
    }
    printf("\n");
    return max;
}

我拟定三个测试区间序列
A: [1,5], [10,15], [5,10], [20,30] // 书中举例的输入,前三个区间边界重叠于5和10
B: [1,4], [10,15], [5,9], [20,30] // A变形成无重叠的四个区间
C: [1,4], [10,15], [5,9], [1,12], [20,30] // B中加一个区间重叠前三个区间

并将区间点排序的结果打印,我转换过来的c++代码运行结果如下:

A:
sort: [1, 5], [5, [10, 10], 15], [20, 30]
olap: 3
B:
sort: [1, 4], [5, 9], [10, 15], [20, 30]
olap: 2
C:
sort: [1, [1, 4], [5, 9], [10, 12], 15], [20, 30]
olap: 3

从运行结果中可以看到,对于A序列作者因为作弊将count初始值为1得到了结果3。对于B序列,如我上面分析,它对并无重叠的区间序列,返回了2。对于C序列,有4个重叠区间,却只返回了3。

下面的代码是修改后的版本,我对比较方法加入次级条件,以及改换重叠判定处理。

int getOverlappingCount_revision(std::vector<Interval>& A)
{
    int olap = 0;
    int count = 0;
    std::vector<Point> points;
    points.resize(A.size() * 2);
    for(int i = 0; i < A.size(); ++i)
    {
        points[2*i].value = A[i].start;
        points[2*i].type = 0;
        points[2*i+1].value = A[i].end;
        points[2*i+1].type = 1;
    }
    // comparation
    struct comparation
    {
        bool operator() (Point& _L, Point& _R)
        {
            if(_L.value == _R.value)
                return _L.type < _R.type;
            return _L.value < _R.value;
        }
    };
    std::sort(points.begin(), points.end(), comparation());
    for(int i = 0; i < points.size(); ++i)
    {
        printf("%s%d%s, ", points[i].type ? "":"[", points[i].value, points[i].type ? "]":"");
        if(points[i].type == 0)
        {
            count++;
            if(count > 1)
            {
                if(olap == 0)
                    olap = 2;
                else
                    olap += 1;
            }
        }
        else
        {
            count--;
        }
    }
    printf("\n");
    return olap;
}

将区间点排序的结果打印,代码运行结果如下:

A:
sort: [1, [5, 5], [10, 10], 15], [20, 30]
olap: 3
B:
sort: [1, 4], [5, 9], [10, 15], [20, 30]
olap: 0
C:
sort: [1, [1, 4], [5, 9], [10, 12], 15], [20, 30]
olap: 4

 

下面是我另一版本的代码,并不对区间点排序,而只对区间起点排序,一边合并区间一边对重叠区间进行计数。

int getOverlappingCount_interval(std::vector<Interval>& _A)
{
    int olap = 0;
    int count = 0;
    std::vector<Interval> A = _A;

    // comparation
    struct comparation
    {
        bool operator() (Interval& _L, Interval& _R)
        {
            return _L.start < _R.start;
        }
    };
    std::sort(A.begin(), A.end(), comparation());
    Interval merge = A[0];
    Interval next(0, 0);
    for(int i = 1; i < A.size(); ++i)
    {
        next = A[i];
        if(merge.end >= next.start)
        {
            merge.end = std::max(merge.end, next.end);
            if(olap == 0)
                olap = 2;
            else
                olap++;
        }
        else
        {
            printf("[%d, %d], ", merge.start, merge.end);
            merge = next;
        }
    }
    printf("[%d, %d]\n", merge.start, merge.end);
    return olap;
}

将合并后的不重叠区间打印,代码运行结果如下:

A:
sort: [1, 15], [20, 30]
olap: 3
B:
sort: [1, 4], [5, 9], [10, 15], [20, 30]
olap: 0
C:
sort: [1, 15], [20, 30]
olap: 4

 

书中给这一题的难度为2星简单,可能因此作者不太关心,而太过随意。

 

求重叠区间个数,某书某题错例分析

标签:

原文地址:http://www.cnblogs.com/bbqzsl/p/5104309.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!