Recursion's general principle is to solve a problem by assuming a smaller problem can be solved, and accounting for the difference between the smaller problem and the one currently being asked to solve.
Many recursive algorithms, e.g. recursive factorial, assume a smaller-by-1 problem and then given that answer, solve the delta between that (smaller by one) answer and the requested answer, e.g. by multiplying the smaller problem's answer by N.
In the case of your question, the approach is to solve not just one but two smaller problems and combine the answer using merge. The smaller problems are subdivisions of the larger problem at
m, which is half of the range being asked to solve.
By the way, there are many different ways that merge sort is coded up. It partly depends on whether your language efficiently supports slices of arrays (or not).
If it does, the the merge sort is typically asked to sort a given array at each invocation, where the array to sort is really a subset of a larger array, but once passed as an argument, is seen going from 0 to some .length.
If the language does not allow for slices, as might be the case in your question, then the merge sort is directed to sort an explicitly identified sub range of the array (by passing the additional start (
l) and end (
r) parameters) instead of turning the portion to sort into another array (slice) and passing only that.
In any case it should be relatively easy to see that
m is chosen about half way between
r. So, the first of the two recursive calls asks to sort subrange
m, and the second, the subrange from
r. Once merged, this accomplishes the requested sort of the full
r subrange that was requested.
The initial call will have
r as the full length, so the first call is asked to sort the whole array, and in turn asks its recursive helpers to sort about half the array. They in turn subdivide the slice of the array they are asked to sort into about half as well... Eventually, the whole range of the original array is sorted and merged.
Many recursive algorithms use more than one (self) recursive call. For example, recursive tree traversals operate in a similar manner: A post order traversal visits the node itself last after recursively calling itself on left and on right. You might see the subdivision into two smaller problems more easily with the recursion of a tree traversal because there is much less work involved in identifying the smaller problem to solve first (there's just left and right).
But the merge sort does the same thing: for each range it is asked to sort, it first (using recursive invocation) sorts the left half, then the right half, then merges. It has to identify the halves using a bit of arithmetic, which differentiates it from the otherwise similar patterned tree traversal.
A variant of merge sort could subdivide the array into three slices, sorting each one (recursively, of course) then merging. The merge part would require an additional parameter (for the chosen third portion, e.g.