📜  Klee算法(线段的并集长度)

📅  最后修改于: 2021-05-04 12:05:26             🧑  作者: Mango

给定直线上线段的开始和结束位置,任务是将所有给定线段的并集并找出这些线段所覆盖的长度。
例子:

Input : segments[] = {{2, 5}, {4, 8}, {9, 12}}
Output : 9 
Explanation:
segment 1 = {2, 5}
segment 2 = {4, 8}
segment 3 = {9, 12}
If we take the union of all the line segments,
we cover distances [2, 8] and [9, 12]. Sum of 
these two distances is 9 (6 + 3)

方法:

该算法由Klee于1977年提出。该算法的时间复杂度为O(N log N)。已经证明该算法是最快的(渐近地),并且不能以更好的复杂性来解决该问题。

描述 :
1)将所有线段的所有坐标放入辅助数组点[]。
2)根据坐标值对它进行排序。
3)排序的附加条件–如果坐标相等,则插入任意段的左坐标而不是右坐标。
4)现在遍历整个数组,并使用计数器“ count”重叠的段。
5)如果计数大于零,则将结果添加到点[i] –点[i-1]之间的差。
6)如果当前元素属于左端,我们增加“ count”,否则减少它。
插图:

Lets take the example :
segment 1 : (2,5)
segment 2 : (4,8)
segment 3 : (9,12)

Counter = result = 0;
n = number of segments = 3;

for i=0,  points[0] = {2, false}
          points[1] = {5, true}
for i=1,  points[2] = {4, false}
          points[3] = {8, true}
for i=2,  points[4] = {9, false}
          points[5] = {12, true}

Therefore :
points = {2, 5, 4, 8, 9, 12}
         {f, t, f, t, f, t}

after applying sorting :
points = {2, 4, 5, 8, 9, 12}
         {f, f, t, t, f, t}

Now,
for i=0, result = 0;
         Counter = 1;

for i=1, result = 2;
         Counter = 2;

for i=2, result = 3;
         Counter = 1;

for i=3, result = 6;
         Counter = 0;

for i=4, result = 6;
         Counter = 1;

for i=5, result = 9;
         Counter = 0;

Final answer = 9;
C++
// C++ program to implement Klee's algorithm
#include
using namespace std;
 
// Returns sum of lengths covered by union of given
// segments
int segmentUnionLength(const vector<
                          pair  > &seg)
{
    int n = seg.size();
 
    // Create a vector to store starting and ending
    // points
    vector  > points(n * 2);
    for (int i = 0; i < n; i++)
    {
        points[i*2]     = make_pair(seg[i].first, false);
        points[i*2 + 1] = make_pair(seg[i].second, true);
    }
 
    // Sorting all points by point value
    sort(points.begin(), points.end());
 
    int result = 0; // Initialize result
 
    // To keep track of counts of
    // current open segments
    // (Starting point is processed,
    // but ending point
    // is not)
    int Counter = 0;
 
    // Trvaerse through all points
    for (unsigned i=0; i > segments;
    segments.push_back(make_pair(2, 5));
    segments.push_back(make_pair(4, 8));
    segments.push_back(make_pair(9, 12));
    cout << segmentUnionLength(segments) << endl;
    return 0;
}


Python
# Python program for the above approach
 
def segmentUnionLength(segments):
   
    # Size of given segments list
    n = len(segments)
     
    # Initialize empty points container
    points = [None] * (n * 2)
     
    # Create a vector to store starting
    # and ending points
    for i in range(n):
        points[i * 2] = (segments[i][0], False)
        points[i * 2 + 1] = (segments[i][1], True)
         
    # Sorting all points by point value
    points = sorted(points, key=lambda x: x[0])
     
    # Initialize result as 0
    result = 0
     
    # To keep track of counts of current open segments
    # (Starting point is processed, but ending point
    # is not)
    Counter = 0
     
    # Traverse through all points
    for i in range(0, n * 2):
       
        # If there are open points, then we add the
        # difference between previous and current point.
        if (i > 0) & (points[i][0] > points[i - 1][0]) &  (Counter > 0):
            result += (points[i][0] - points[i - 1][0])
             
        # If this is an ending point, reduce, count of
        # open points.
        if points[i][1]:
            Counter -= 1
        else:
            Counter += 1
    return result
 
 
# Driver code
if __name__ == '__main__':
    segments = [(2, 5), (4, 8), (9, 12)]
    print(segmentUnionLength(segments))


输出
9