The following is a practice interview question that was given to me by someone, and I\'m not sure what the best solution to this is:
Given a set of ranges
Your assignment intrigued me, so I wrote a C++ program that solves the problem by iterating through the ranges that overlap the left side of the target range, and recursively searches for the smallest number of ranges that covers the remaining (right side) of the target range.
A significant optimization to this algorithm (not shown in this program) would be to, for each recursive level, use the range that overlaps the left side of the target range by the largest amount, and discarding from further consideration all ranges that overlap the left side by smaller amounts. By employing this rule, I believe there would be at most a single descent into the recursive call tree. Such an optimization would produce an algorithm having complexity O(n log(n)). (n to account for the depth of recursion, and log(n) to account for the binary search to find the range having the most overlap.)
This program produces the following as output:
{ (3, 9) (9, 12) (11, 14) }
Here is the program:
#include // for std::pair
#include // for std::vector
#include // for std::cout & std::endl
typedef std::pair range;
typedef std::vector rangelist;
// function declarations
rangelist findRanges (range targetRange, rangelist candidateRanges);
void print (rangelist list);
int main()
{
range target_range = { 3, 13 };
rangelist candidate_ranges =
{ { 1, 4 }, { 30, 40 }, { 20, 91 }, { 8, 10 }, { 6, 7 }, { 3, 9 }, { 9, 12 }, { 11, 14 } };
rangelist result = findRanges (target_range, candidate_ranges);
print (result);
return 0;
}
// Recursive function that returns the smallest subset of candidateRanges that
// covers the given targetRange.
// If there is no subset that covers the targetRange, then this function
// returns an empty rangelist.
//
rangelist findRanges (range targetRange, rangelist candidateRanges)
{
rangelist::iterator it;
rangelist smallest_list_so_far;
for (it = candidateRanges.begin (); it != candidateRanges.end (); ++it) {
// if this candidate range overlaps the beginning of the target range
if (it->first <= targetRange.first && it->second >= targetRange.first) {
// if this candidate range also overlaps the end of the target range
if (it->second >= targetRange.second) {
// done with this level - return a list of ranges consisting only of
// this single candidate range
return { *it };
}
else {
// prepare new version of targetRange that excludes the subrange
// overlapped by the present range
range newTargetRange = { it->second + 1, targetRange.second };
// prepare new version of candidateRanges that excludes the present range
// from the list of ranges
rangelist newCandidateRanges;
rangelist::iterator it2;
// copy all ranges up to but not including the present range
for (it2 = candidateRanges.begin (); it2 != it; ++it2) {
newCandidateRanges.push_back (*it2);
}
// skip the present range
it2++;
// copy the remainder of ranges in the list
for (; it2 != candidateRanges.end(); ++it2) {
newCandidateRanges.push_back (*it2);
}
// recursive call to find the smallest list of ranges that cover the remainder
// of the target range not covered by the present range
rangelist subList = findRanges (newTargetRange, newCandidateRanges);
if (subList.size () == 0) {
// no solution includes the present range
continue;
}
else if (smallest_list_so_far.size () == 0 || // - first subList that covers the remainder of the target range
subList.size () < smallest_list_so_far.size ()) // - this subList is smaller than all previous ones checked
{
// add the present range to the subList, which represents a solution
// (though possibly not optimal yet) at the present level of recursion
subList.push_back (*it);
smallest_list_so_far = subList;
}
}
}
}
return smallest_list_so_far;
}
// print list of ranges
void print (rangelist list)
{
rangelist::reverse_iterator rit;
std::cout << "{ ";
for (rit = list.rbegin (); rit != list.rend (); ++rit) {
std::cout << "(" << rit->first << ", " << rit->second << ") ";
}
std::cout << "}" << std::endl;
}