(Points Covering Intervals: prob # 13.12 in ver 1.2.1)
prior to posting, i checked my solution against the latest solution on the website against 999 randomly generated tests each run multiple times.
my solution gets rid of extra collections like covering_list, covered_set, sorts only half the items (sorting n vs 2n items… because i only sort the right endpoints…), and i think shorter/simpler, so maybe easier to understand?)
existing given solution in the website (only the parts that’s relevant):
public static class EndPoint implements Comparable<EndPoint> {
public Interval ptr;
public boolean isLeft;
public EndPoint(Interval i, boolean il) {
ptr = i;
isLeft = il;
}
public int compareTo(EndPoint that) {
int a = isLeft ? ptr.left : ptr.right, b = that.isLeft ? that.ptr.left : that.ptr.right;
if (a != b) {
return a - b;
}
// Give preference to left endpoint.
if (isLeft && !that.isLeft) {
return -1;
} else if (!isLeft && that.isLeft) {
return 1;
} else {
return 0;
}
}
}
public static List<Integer> findMinimumVisits(Interval[] intervals) {
List<EndPoint> endpoints = new ArrayList<>();
for (Interval aI : intervals) {
endpoints.add(new EndPoint(aI, true));
endpoints.add(new EndPoint(aI, false));
}
Collections.sort(endpoints);
return findMinimumVisitsHelper(endpoints);
}
private static List<Integer> findMinimumVisitsHelper(List<EndPoint> endpoints) {
List<Integer> S = new ArrayList<>(); // A minimum set of visit times.
Set<Interval> covered = new HashSet<>();
List<Interval> covering = new ArrayList<>();
for (EndPoint e : endpoints) {
if (e.isLeft) {
covering.add(e.ptr);
} else if (!covered.contains(e.ptr)) {
// e's interval has not been covered.
S.add(e.ptr.right);
// Adds all intervals in covering to covered.
covered.addAll(covering);
covering.clear(); // e is contained in all intervals in
// covering.
}
}
return S;
}
my solution:
public static List<Integer> minVisitsAlt(Interval[] intervals) {
Arrays.sort(intervals, new Comparator<Interval>() {
public int compare(Interval o1, Interval o2) {
return o1.right - o2.right;
}
});
List<Integer> ret = new ArrayList<Integer>();
Integer lastVisit = 0;
for (Interval interval : intervals) {
if (!ret.isEmpty() && lastVisit >= interval.left) {
continue;
}
lastVisit = interval.right;
ret.add(lastVisit);
}
return ret;
}
so basically, the key difference between two algorithms is the use of lastVisit_integer vs covered_set & covered_list for checking whether a given interval is already covered by previous visits; this also gets rid of needing to sort the left endpoint.
given the task sorted by their right endpoints, only thing we need to check whether the given task is already covered by previous visits is to check the given task’s left endpoint against the most recent lastVisit integer;
we know that any interval whose left endpoint is <= than the lastVisit value is already covered.
if above is not true, then that means there is an interval whose left endpoint <= lastVisit && right endpoint >= lastVisit but not covered… which is contradictory so can’t be true.
if left endpoint is bigger than the lastVisit, then the given interval is not covered, and among the uncovered intervals, this one has the earliest right endpoint, so we have to visit that right endpoint so add that.
then loop.
so lastVisit is the only value we need to keep; no need for covering_list or covered_set. thereby making the solution simpler; (and less memory due to no need of extra covered_set and covering_list)