We can't find the internet
Attempting to reconnect
Something went wrong!
Hang in there while we get back on track
Post 47
Hacker Rank 21 - Min of a Range
Published on: 2025-07-15
Tags:
elixir, Side Project, Hacker Rank
Range Minimum Query (RMQ) is a set of problems which deals with finding a property (here minimum) of a range. Segment Tree can be very helpful when solving with such problems. A segment tree is a tree like data structure which is used to store the information about intervals. Here's the [(wiki link)] of it. You are given a array of N integers, arr[0], arr[1], .., arr[(N-1)]. And you are given a list of ranges. For each range, (l, r) you have to find the minimum value between range arr[l], arr[l+1], arr[l+2], .., arr[r]. Sample Input 10 5 10 20 30 40 11 22 33 44 15 5 0 5 1 2 8 9 0 9 4 6 Sample Output 10 20 5 5 11 So for this we are using a segment tree that will store the min value for any range in the tree. So we will need to not only build the tree but the search algorithm for finding the right node. Let's build the tree first, this will require that the initial array will be of the form: %{min: value, interval: [index, index], left: nil, right: nil} This si becuase a segment tree is built from the children up and each node will have to have the interval: [index, index] as a single length interval is valid, left/right: nil as they will have no children, and min: value as the min of a single element is the element. def build_recursive([only]), do: only # This is the case where when building the left and right has a single element left over. def build_recursive(children) do # We need to pair together each child and then build the parent from the children Enum.chunk_every(children, 2) |> Enum.map(fn # This is for the 2 cases as we will have 2 nodes or a single node in the chunk_every # Once you have the pairs you need to build the parent [left, right] -> build_parent(left, right) [solo] -> build_parent(solo) end) # You need to continue to build the rest of the tree until you get to a single element grandparent |> build_recursive() end # If you get a single element you want to return the node def build_parent(solo), do: solo # You must build the new interval based off the start of the left and the end of the right # then you need to find the min of the 2 children. def build_parent(left, right) do %{ interval: [hd(left.interval), Enum.at(right.interval, -1)], min: min(left.min, right.min), right: right, left: left } end Please look at the comment for each line but it simplifies to taking pairs of nodes and creating a parent off those nodes. If you have 2 intervals and want to find the min of the 2 combined you will need the start of the lowest and the end of the highest and then compare the 2. Once you have the tree built you need to traverse the tree in order to find the min for any given interval. One thing to keep in mind here is that the first node is the min of the entire tree, the left side will be all intervals from the start to the mid and the right is the mid to the end. With this in mind you will be able to traverse the tree to find the right value with: # This implies that the range is out of the bounds of the original array. def search(%{interval: [start, last], min: _min, left: _left, right: _right}, [q_start, q_end]) when q_end < start or q_start > last, do: nil # This is for when you find the right interval in the tree. You want a little fuzzy logic as it will not # always be in the right bounds def search(%{interval: [start, last], min: min, left: _left, right: _right}, [q_start, q_end]) when q_start <= start and q_end >= last, do: min # This is the logic for continuing to search the tree for the right value. def search(%{interval: [_start, _last], min: _min, left: left, right: right}, [begin, ending]) do safe_min(search(left, [begin, ending]), search(right, [begin, ending])) end # This is not needed as you should with the right inputs never need to worry about a bad result but # this will help to be sure that you are always returning a value. defp safe_min(nil, val), do: val defp safe_min(val, nil), do: val defp safe_min(a, b), do: min(a, b)
