Given a list of intervals that may be intersecting, merge those that are intersecting.
For example, [2 to 19, 18 to 25] becomes [2 to 25], but [2 to 19, 22 to 25] remains as-is.
DO it efficiently and functionally.
Test cases in Scala
assert(mergeIntervals(Nil) == Nil) assert(mergeIntervals(List(2 to 43)) == List(2 to 43)) assert(mergeIntervals(List(2 to 19, 20 to 30)) == List(2 to 19, 20 to 30)) assert(mergeIntervals(List(2 to 19, 18 to 30)) == List(2 to 30)) assert(mergeIntervals(List(2 to 19, 18 to 21, 20 to 25)) == List(2 to 25)) assert( mergeIntervals(List(2 to 19, 18 to 19, 20 to 25)) == List(2 to 19, 20 to 25) )
Algorithm in Scala
29 lines of Scala (compatible versions 2.13 & 3.0), showing how concise Scala can be!
We use Tail Recursion and FP style traversal. Firstly, it is important to sort the intervals to know which ones are the most likely to intersect.
The algorithm we follow keeps track of the ranges that have not been handled yet ('remaining'), as well as a list of collected ranges. When we iterate, we match the first remaining range against the first most recently collected range. If they match, then the head of the 'collected' list is replaced with the merged range. (this is © from www.scala-algorithms.com)
For representation, we are using Scala's typical Range class.
Scala concepts & Hints
Def Inside Def
A great aspect of Scala is being able to declare functions inside functions, making it possible to reduce repetition.
It is also frequently used in combination with Tail Recursion.
The 'Option' type is used to describe a computation that either has a result or does not. In Scala, you can 'chain' Option processing, combine with lists and other data structures. For example, you can also turn a pattern-match into a function that return an Option, and vice-versa!
In Scala, the 'Ordering' type is a 'type class' that contains methods to determine an ordering of specific types.
Pattern matching in Scala lets you quickly identify what you are looking for in a data, and also extract it.
(1 to n)syntax produces a "Range" which is a representation of a sequence of numbers.
Stack safety is present where a function cannot crash due to overflowing the limit of number of recursive calls.
This function will work for n = 5, but will not work for n = 2000 (crash with java.lang.StackOverflowError) - however there is a way to fix it :-)
In Scala Algorithms, we try to write the algorithms in a stack-safe way, where possible, so that when you use the algorithms, they will not crash on large inputs. However, stack-safe implementations are often more complex, and in some cases, overly complex, for the task at hand.
In Scala, tail recursion enables you to rewrite a mutable structure such as a while-loop, into an immutable algorithm.