I have a list of Intervals and a second interval which is used to bound the other intervals. For example:
[[4,7],[5,7]]
bounded by [0,10]
I need an algorithm, which is able to shift the intervals of the list with as little motion as possible so that no interval is overlapping each other and every interval is in the boundary.
Output: [[4,7],[7,9]]
other Examples:
[[6,8],[6,9]]
bounded by [0,10]
Output: [[5,7],[7,10]]
[[0,1],[5,6],[6,8],[6,9]]
bounded by [0,10]
Output: [[0,1],[4,5],[5,7],[7,10]]
I tried to write this algorithm in C#, but when 3 or more intervals are near the boundary, it gets stuck in a infinite recursion.
public class Bookshelf
{
public List<Tuple<int, int>> Intervals { get; private set; }
public Tuple<int, int> Boundary { get; private set; }
public Bookshelf(List<Tuple<int, int>> intervals, Tuple<int, int> boundary)
{
Intervals=intervals;
Boundary=boundary;
Intervals.Sort((a, b) =>
{
int compareStart = a.Item1.CompareTo(b.Item1);
return compareStart != 0 ? compareStart : a.Item2.CompareTo(b.Item2);
});
foreach (var overlap in FindOverlaps())
HandleOverlap(overlap);
}
public static int Range(Tuple<int, int> interval) => interval.Item2 - interval.Item1;
public bool IsEnoughSpace(Tuple<int, int> interval) => (Intervals.Sum(item => Range(item)) + Range(interval)) <= Range(Boundary);
public int IsInBound(Tuple<int, int> interval) => (Boundary.Item1 <= interval.Item1) && (Boundary.Item2 >= interval.Item2) ? 0 : (Boundary.Item1 > interval.Item1) ? -1 : 1;
public void AddInterval(Tuple<int, int> interval)
{
if (!IsEnoughSpace(interval)) throw new Exception("Not enough space");
if (IsInBound(interval) != 0) throw new Exception("Not in Bound");
Intervals.Add(interval);
Intervals.Sort((a, b) =>
{
int compareStart = a.Item1.CompareTo(b.Item1);
return compareStart != 0 ? compareStart : a.Item2.CompareTo(b.Item2);
});
foreach(var overlap in FindOverlaps())
HandleOverlap(overlap);
}
private List<Tuple<Tuple<int, int>, Tuple<int, int>>> FindOverlaps()
{
List<Tuple<Tuple<int, int>, Tuple<int, int>>> overlaps = new List<Tuple<Tuple<int, int>, Tuple<int, int>>>();
int currentStart = Intervals[0].Item1;
int currentEnd = Intervals[0].Item2;
for (int i = 1; i < Intervals.Count; i++)
{
int start = Intervals[i].Item1;
int end = Intervals[i].Item2;
if (start < currentEnd && currentStart < end)
{
overlaps.Add(Tuple.Create(Intervals[i-1], Intervals[i]));
currentEnd = Math.Max(currentEnd, end);
}
else
{
currentStart = start;
currentEnd = end;
}
}
return overlaps;
}
private void HandleOverlap(Tuple<Tuple<int, int>, Tuple<int, int>> pair)
{
Tuple<int, int> lower = pair.Item1;
Tuple<int, int> upper = pair.Item2;
int upperRange = Range(upper);
int lowerRange = Range(lower);
Tuple<int, int> temp = Tuple.Create(lower.Item2, lower.Item2 + upperRange);
if (IsInBound(temp) == 0)
{
int idx = Intervals.IndexOf(upper);
Intervals[idx] = temp;
foreach (var overlap in FindOverlaps())
HandleOverlap(overlap);
}
else if(IsInBound(temp) < 0)
{
int idx = Intervals.IndexOf(upper);
Intervals[idx] = Tuple.Create(lower.Item2, lower.Item2 + upperRange);
idx = Intervals.IndexOf(lower);
Intervals[idx] = Tuple.Create(Boundary.Item1, Boundary.Item1 + lowerRange);
foreach (var overlap in FindOverlaps())
HandleOverlap(overlap);
}
else
{
int idx = Intervals.IndexOf(upper);
Intervals[idx] = Tuple.Create(Boundary.Item2 - upperRange, Boundary.Item2);
idx = Intervals.IndexOf(lower);
Intervals[idx] = Tuple.Create(upper.Item1 - lowerRange, upper.Item1);
foreach (var overlap in FindOverlaps())
HandleOverlap(overlap);
}
}
If you know the solution, can also write it in pseudocode.
EDIT: I also tried other ways to solve this issue.
public Bookshelf(List<Tuple<int, int>> intervals, Tuple<int, int> boundary)
{
Intervals = intervals;
Boundary = boundary;
HandleOverlap();
}
private void HandleOverlap()
{
Intervals.Sort((a, b) => a.Item1.CompareTo(b.Item1));
List<Tuple<int, int>> adjustedIntervals = new List<Tuple<int, int>>();
int lastEnd = Boundary.Item1;
foreach (var interval in Intervals)
{
int start = interval.Item1;
int end = interval.Item2;
if (start < lastEnd)
{
start = lastEnd;
end = start + (interval.Item2 - interval.Item1);
}
if (end > Boundary.Item2)
{
end = Boundary.Item2;
start = end - (interval.Item2 - interval.Item1);
}
lastEnd = end;
adjustedIntervals.Add(Tuple.Create(start, end));
}
Intervals = adjustedIntervals;
}
This Approach worked better than my attempt before. However it cant remove overlaps at the upper bound. Here is example I used
[(5,9),(7,10)] bounded by (0,10)
Expected Output: [(3,7),(7,10)]
Actual Output: [(5,9),(7,10)]