这是您可以尝试的一种方法(在 C# 中。我没有测试过,所以请原谅拼写错误等;只需采用“想法”/策略)。
性能为 O(N log m),其中 m 是您将创建的不相交“阴影”的数量。所以,最坏的情况(如果所有线段的阴影都不相交)你会有 O(N logN),但是当你只有很少的阴影时,它本质上是 O(N)。
public class LineSegment
{
public int Begin;
public int End;
// assumed to INCLUDE Begin but EXCLUDE End, so that
// length = End-Begin
public LineSegment Clone()
{
LineSegment clone = new LineSegment();
clone.Begin=this.Begin;
clone.End = this.End;
return clone;
}
}
public int TotalShadow(LineSegment[] segments)
{
// Class LineSegment has int members Begin and End, and Clone method to create a (shallow) Copy.
// Can/should be adapted if we're dealing with LineSegments with double/float coordinates.
// Easy special cases: no segements at all, or precisely one.
int N = segments.Length;
if (N == 0)
return 0;
else if (N == 1)
return (segments[0].End - segments[0].Begin);
// build a list of disjoint "shadows", cast onto the x-axis by all line segments together,
// sorted by their "Begin" (leftmost coordinate).
List<LineSegment> shadows = new List<LineSegment>();
// Initialize with the first segment, for convenient iteration below.
shadows.Add(segments[0].Clone());
for (int k = 1; k < N; ++k) // start at #1: we handled #0 already.
{
// find its position (Begin) in relation to the existing shadows (binary search).
int xBegin = segments[k].Begin;
int jLow = 0;
int xLow = shadows[jLow].Begin;
int jHigh, xHigh;
if (xBegin <= xLow)
jHigh = jLow; // avoid any more binary searching below
else
{
jHigh = shadows.Count - 1;
xHigh = shadows[jHigh].Begin;
if (xBegin >= xHigh)
jLow = jHigh; // avoid any more binary searching below
}
while (jHigh - jLow > 1)
{
int jTry = (jLow + jHigh) / 2;
int xTry = shadows[jTry].Begin;
if (xTry <= xBegin)
jLow = jTry;
else
jHigh = jTry;
}
// If it starts BEFORE "low" we create a new one: insert at jLow;
// Elseif x falls INSIDE "low", we merge it with low;
// ELSE we create a new shadow "between" low and high (as regards Begin)
// In all cases we'll make sure jLow points to the applicable shadow (new or existing).
// Next we'll check whether it overlaps with adjacent higher-lying shadows; if so: merge.
if (xBegin < shadows[jLow].Begin)
shadows.Insert(jLow, segments[k].Clone()); // jLow now points to the inserted item
else if (xBegin <= shadows[jLow].End)
{ // merge: extend existing low if applicable.
if (segments[k].End > shadows[jLow].End)
shadows[jLow].End = segments[k].End;
}
else // if (xBegin > shadows[jLow].End)
shadows.Insert(++jLow, segments[k].Clone()); // jLow increased to point to the inserted item.
// Try to merge, starting at the next higher lying shadow.
jHigh = jLow + 1;
while (jHigh < N && shadows[jLow].End >= shadows[jHigh].Begin)
jHigh++; // jHigh will point to the first one that we do NOT merge with.
if (jHigh > jLow + 1) // any merges?
{
if (shadows[jHigh - 1].End > shadows[jLow].End)
shadows[jLow].End = shadows[jHigh - 1].End; // set the appropriate End.
for (int jRemove = jHigh - 1; jRemove > jLow; --jRemove)
shadows.RemoveAt(jRemove); // Remove all shadaow-entries that we've merged with.
}
}
// Wrap up.
int shadowTotal = 0;
foreach (LineSegment shadow in shadows)
shadowTotal += (shadow.End - shadow.Begin);
return shadowTotal;
}