标签:
大意就是我淘钱买了一本题集,觉得书中有些地方作者太随意,例子错得不严谨,一度阻碍阅读。作为消费者不得不拿出来说一说。
本文本着不迷信,实事求是精神。
本文编排如下:
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