האלגוריתם של קליי (אורך האיחוד של מקטעי קו)

בהינתן עמדות התחלה וסיום של קטעים על קו, המשימה היא לקחת את האיחוד של כל הקטעים הנתונים ולמצוא אורך המכוסה על ידי קטעים אלה.
דוגמאות:   

  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) כעת עברו על כל המערך עם 'ספירת' המונה של קטעים חופפים. 
5) אם הספירה גדולה מאפס אז התוצאה מתווספת להפרש בין הנקודות[i] - נקודות[i-1]. 
6) אם האלמנט הנוכחי שייך לקצה השמאלי אנו מגדילים 'ספירה' אחרת מצמצמים אותו.
אִיוּר:  

Lets take the example : segment 1 : (25) segment 2 : (48) segment 3 : (912) 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      <  int    int  >     >     &  seg  )   {      int     n     =     seg  .  size  ();      // Create a vector to store starting and ending      // points      vector      <  pair      <  int       bool  >     >     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  ;      // Traverse through all points      for     (  unsigned     i  =  0  ;     i   <  n  *  2  ;     i  ++  )      {      // If there are open points then we add the      // difference between previous and current point.      // This is interesting as we don't check whether      // current point is opening or closing      if     (  Counter  )      result     +=     (  points  [  i  ].  first     -         points  [  i  -1  ].  first  );      // If this is an ending point reduce count of      // open points.      (  points  [  i  ].  second  )  ?     Counter  --     :     Counter  ++  ;      }      return     result  ;   }   // Driver program for the above code   int     main  ()   {      vector   <     pair      <  int    int  >     >     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  ;   }   
Java
   // Java program to implement Klee's algorithm   import     java.io.*  ;   import     java.util.*  ;   class   GFG     {      // to use create a pair of segments      static     class   SegmentPair      {      int     x    y  ;      SegmentPair  (  int     xx       int     yy  ){      this  .  x     =     xx  ;      this  .  y     =     yy  ;      }      }      //to create a pair of points      static     class   PointPair  {      int     x  ;      boolean     isEnding  ;      PointPair  (  int     xx       boolean     end  ){      this  .  x     =     xx  ;      this  .  isEnding     =     end  ;      }      }      // creates the comparator for comparing objects of PointPair class      static     class   Comp     implements     Comparator   <  PointPair  >      {          // override the compare() method      public     int     compare  (  PointPair     p1       PointPair     p2  )      {      if     (  p1  .  x      <     p2  .  x  )     {      return     -  1  ;      }      else     {      if  (  p1  .  x     ==     p2  .  x  ){      return     0  ;      }  else  {      return     1  ;      }      }      }      }      public     static     int     segmentUnionLength  (  List   <  SegmentPair  >     segments  ){      int     n     =     segments  .  size  ();      // Create a list to store       // starting and ending points      List   <  PointPair  >     points     =     new     ArrayList   <>  ();      for  (  int     i     =     0  ;     i      <     n  ;     i  ++  ){      points  .  add  (  new     PointPair  (  segments  .  get  (  i  ).  x    false  ));      points  .  add  (  new     PointPair  (  segments  .  get  (  i  ).  y    true  ));      }          // Sorting all points by point value      Collections  .  sort  (  points       new     Comp  ());      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  ;      // Traverse through all points      for  (  int     i     =     0  ;     i      <     2     *     n  ;     i  ++  )      {          // If there are open points then we add the      // difference between previous and current point.      // This is interesting as we don't check whether      // current point is opening or closing      if     (  Counter     !=     0  )      {      result     +=     (  points  .  get  (  i  ).  x     -     points  .  get  (  i  -  1  ).  x  );      }      // If this is an ending point reduce count of      // open points.      if  (  points  .  get  (  i  ).  isEnding  )      {      Counter  --  ;      }      else      {      Counter  ++  ;      }      }      return     result  ;      }      // Driver Code      public     static     void     main     (  String  []     args  )     {      List   <  SegmentPair  >     segments     =     new     ArrayList   <>  ();      segments  .  add  (  new     SegmentPair  (  2    5  ));      segments  .  add  (  new     SegmentPair  (  4    8  ));      segments  .  add  (  new     SegmentPair  (  9    12  ));      System  .  out  .  println  (  segmentUnionLength  (  segments  ));      }   }   // This code is contributed by shruti456rawal   
Python3
   # 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  ))   
C#
   using     System  ;   using     System.Collections  ;   using     System.Collections.Generic  ;   using     System.Linq  ;   // C# program to implement Klee's algorithm   class     HelloWorld     {      class     GFG     :     IComparer   <  KeyValuePair   <  int       bool  >>      {      public     int     Compare  (  KeyValuePair   <  int       bool  >     x    KeyValuePair   <  int       bool  >     y  )      {      // CompareTo() method      return     x  .  Key  .  CompareTo  (  y  .  Key  );      }      }      // Returns sum of lengths covered by union of given      // segments      public     static     int     segmentUnionLength  (  List   <  KeyValuePair   <  int    int  >>     seg  )      {      int     n     =     seg  .  Count  ;      // Create a vector to store starting and ending      // points      List   <  KeyValuePair   <  int       bool  >>     points     =     new     List   <  KeyValuePair   <  int       bool  >>  ();      for  (  int     i     =     0  ;     i      <     2  *  n  ;     i  ++  ){      points  .  Add  (  new     KeyValuePair   <  int       bool  >     (  0    true  ));      }         for     (  int     i     =     0  ;     i      <     n  ;     i  ++  )      {      points  [  i  *  2  ]     =     new     KeyValuePair   <  int       bool  >     (  seg  [  i  ].  Key       false  );      points  [  i  *  2     +     1  ]     =     new     KeyValuePair   <  int       bool  >     (  seg  [  i  ].  Value       true  );      }      // Sorting all points by point value      GFG     gg     =     new     GFG  ();      points  .  Sort  (  gg  );      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  ;      // Traverse through all points      for     (  int     i  =  0  ;     i   <  n  *  2  ;     i  ++  )      {      // If there are open points then we add the      // difference between previous and current point.      // This is interesting as we don't check whether      // current point is opening or closing      if     (  Counter     !=     0  )      result     +=     (  points  [  i  ].  Key     -     points  [  i  -  1  ].  Key  );      // If this is an ending point reduce count of      // open points.      if  (  points  [  i  ].  Value     !=     false  ){      Counter  --  ;      }      else  {      Counter  ++  ;      }      }      return     result  ;      }      static     void     Main  ()     {      List   <  KeyValuePair   <  int    int  >>     segments     =     new     List   <  KeyValuePair   <  int    int  >>     ();      segments  .  Add  (  new     KeyValuePair   <  int    int  >     (  2       5  ));      segments  .  Add  (  new     KeyValuePair   <  int    int  >     (  4       8  ));      segments  .  Add  (  new     KeyValuePair   <  int    int  >     (  9       12  ));      Console  .  WriteLine  (  segmentUnionLength  (  segments  ));      }   }   // The code is contributed by Nidhi goel.    
JavaScript
   // JavaScript program to implement Klee's algorithm   // Returns sum of lengths covered by union of given   // segments   function     segmentUnionLength  (  seg  )   {      let     n     =     seg  .  length  ;      // Create a vector to store starting and ending      // points      let     points     =     new     Array  (  2  *  n  );      for     (  let     i     =     0  ;     i      <     n  ;     i  ++  )      {      points  [  i  *  2  ]     =     [  seg  [  i  ][  0  ]     false  ];      points  [  i  *  2     +     1  ]     =     [  seg  [  i  ][  1  ]     true  ];      }      // Sorting all points by point value      points  .  sort  (  function  (  a       b  ){      return     a  [  0  ]     -     b  [  0  ];      });          let     result     =     0  ;     // Initialize result      // To keep track of counts of       // current open segments      // (Starting point is processed       // but ending point      // is not)      let     Counter     =     0  ;      // Traverse through all points      for     (  let     i  =  0  ;     i   <  n  *  2  ;     i  ++  )      {      // If there are open points then we add the      // difference between previous and current point.      // This is interesting as we don't check whether      // current point is opening or closing      if     (  Counter  )      result     +=     (  points  [  i  ][  0  ]     -     points  [  i  -  1  ][  0  ]);      // If this is an ending point reduce count of      // open points.      if  (  points  [  i  ][  1  ]){      Counter     =     Counter     -     1  ;      }      else  {      Counter     =     Counter     +     1  ;      }      }      return     result  ;   }   let     segments     =     new     Array  ();   segments  .  push  ([  2       5  ]);   segments  .  push  ([  4       8  ]);   segments  .  push  ([  9       12  ]);   console  .  log  (  segmentUnionLength  (  segments  ));   // The code is contributed by Gautam goel (gautamgoel962)   

תְפוּקָה
9 

מורכבות זמן: O(n * log n)
מרחב עזר: עַל)