From f5df67ce621e4aef2a561663b1da7677c6f14069 Mon Sep 17 00:00:00 2001 From: Matej Focko Date: Sun, 7 Aug 2022 15:55:50 +0200 Subject: [PATCH] =?UTF-8?q?4kyu:=20add=20=E2=80=9ESum=20of=20Intervals?= =?UTF-8?q?=E2=80=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Matej Focko --- 4kyu/sum_of_intervals/solution.java | 148 ++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 4kyu/sum_of_intervals/solution.java diff --git a/4kyu/sum_of_intervals/solution.java b/4kyu/sum_of_intervals/solution.java new file mode 100644 index 0000000..a98e84b --- /dev/null +++ b/4kyu/sum_of_intervals/solution.java @@ -0,0 +1,148 @@ +package cw; + +import java.util.TreeSet; + +class Interval { + private static class Range implements Comparable { + private int from; + private int to; + + public Range(int from, int to) { + this.from = from; + this.to = to; + } + + public int length() { + return to - from; + } + + public Range merge(Range other) { + return new Range(Math.min(from, other.from), Math.max(to, other.to)); + } + + public boolean mergeInSitu(Range other) { + if (!canMerge(other)) { + return false; + } + + from = Math.min(from, other.from); + to = Math.max(to, other.to); + return true; + } + + public boolean has(int x) { + return from <= x && x <= to; + } + + public boolean isSubrangeOf(Range other) { + return other.has(from) && other.has(to); + } + + public boolean isPrefixOf(Range other) { + return from <= other.from && other.has(to); + } + + public boolean isSuffixOf(Range other) { + return to >= other.to && has(other.from); + } + + public boolean canMerge(Range other) { + return isSubrangeOf(other) || isPrefixOf(other) || isSuffixOf(other); + } + + @Override + public int compareTo(Range other) { + if (other == null) { + throw new NullPointerException(); + } + + if (from == other.from && to == other.to) { + return 0; + } + + if (to < other.from || from < other.from) { + return -1; + } + + return 1; + } + } + + private static void insertRange(TreeSet ranges, Range range) { + for (Range r : ranges) { + if (r.canMerge(range)) { + ranges.remove(r); + ranges.add(r.merge(range)); + return; + } + } + + ranges.add(range); + } + + private static boolean mergeRanges(TreeSet ranges) { + for (Range x : ranges) { + for (Range y : ranges) { + if (x == y) { + continue; + } + + if (x.canMerge(y)) { + ranges.remove(x); + ranges.remove(y); + ranges.add(x.merge(y)); + return true; + } + } + } + + return false; + } + + public static int sumIntervals(int[][] intervals) { + if (intervals == null) { + return 0; + } + + TreeSet ranges = new TreeSet<>(); + + // Go through the intervals and construct a set out of them + // when adding each interval, try to merge it in-situ with any of the + // existing intervals + for (int[] interval : intervals) { + insertRange(ranges, new Range(interval[0], interval[1])); + } + + // then iteratively merge the set of intervals while possible + boolean hasChanged = true; + while (hasChanged) { + hasChanged = mergeRanges(ranges); + } + + for (Range r : ranges) { + System.out.format("%d, %d; ", r.from, r.to); + } + System.out.println(); + + return ranges.stream().map(Range::length).reduce(Integer::sum).orElse(0); + } + + public static void main(String[] args) { + // null argument + System.out.format("%d == %d\n", Interval.sumIntervals(null), 0); // => 0 + + // empty intervals + System.out.format("%d == %d\n", Interval.sumIntervals(new int[][]{}), 0); // => 0 + System.out.format("%d == %d\n", Interval.sumIntervals(new int[][]{{2,2}, {5,5}}), 0); // => 0 + + // disjoined intervals + System.out.format("%d == %d\n", Interval.sumIntervals(new int[][]{ + {1,2},{3,5} + }), 3); // => (2-1) + (5-3) = 3 + + // overlapping intervals + System.out.format("%d == %d\n", Interval.sumIntervals(new int[][]{ + {1,4},{3,6},{2,8} + }), 7); // [1,8] => 7 + } +}