I have the following ranges in interval [1-15]
I want to find the overlap ranges between people 1 & 2.
Person1 [1, 3] [5, 10] Person2 [2, 4] [8, 15]
Sort the starts and ends of the ranges.. keeping information alongside as to whether its a range-start or finish... for your example you'll get this:
1 start
2 start
3 end
4 end
5 start
8 start
10 end
15 end
Now loop over these points and keep a counter.. +1 for a start -1 for an end. This counter is the number of overlapping segments at any time. If you want the boundaries you need to test each time you increase or decrease the counter. If you increase it from 1 to 2 this is a start of an overlapping range.. the end of the overlapping range will be when you decrease the counter from 2 to 1
Martin
Thanks for the clarification. What about something like this...
public static IList<Range> GetListIntersections(IList<Range> rangeList1, IList<Range> rangeList2)
{
var intersection = new List<Range>();
//add intersection of each range
foreach (var x in rangeList1)
{
foreach (var y in rangeList2)
{
var intersect = GetIntersection(x, y);
if (intersect != null)
{
intersection.Add(intersect);
}
}
}
//remove ranges that are subsets of other ranges
intersection.RemoveAll(x => intersection.Any(y => y != x && y.Start >= x.Start && y.End <= x.End));
return intersection;
}
public static Range GetIntersection(Range range1, Range range2)
{
int greatestStart = range1.Start > range2.Start ? range1.Start : range2.Start;
int smallestEnd = range1.End < range2.End ? range1.End : range2.End;
//no intersection
if (greatestStart > smallestEnd)
{
return null;
}
return new Range { Start = greatestStart, End = smallestEnd };
}
I don't understand how you're looping by 'person1's range, then by person2's ranges' - I'm not sure what that means without seeing the code.
I can't see how you would get better than O(n), but you can iterate through the range only once. A better data structure might be a bool[]
or BitArray
.
var person1 = new bool[15] { /* set values */ };
var person2 = new bool[15] { /* set values */ };
var overlap = new bool[15];
for (int i = 0; i < 15; i++)
{
overlap[i] = person1[i] && person2[i];
}
Have a look at the merge step of the mergesort algorithm. If the ranges for each person are sorted this method can be adapted to compute the overlaps very easily.
Loop
Get the range that starts next (R1)
if the next range of the other person (R2) starts before R1 ends
Add the range from begin of R2 and min( end of R1 end of R2 ) to results
Increase the counter for the person which gave you R1
If your ranges are known to be non adjacent (i.e. if there is always at least one number between to consecutive ranges). The solution will also be. Else you might need an extra compaction step to ensure that adjacent ranges will be put into one range.
The nice thing is that this works for any ordered type and not just ints, and you can intersect any number of ranges very fast ( O(n+m) ).