The problem (from MIT 6.006 course) is the following:
In this problem, you are given a $2D$ array of integers $A$ that has $n$ rows and $m$ columns. (Assume $m$ is much (e.g., exponentially) smaller than $n$.) Array $A$ has one special property: the integers in every row of $A$ and every column of $A$ are non-decreasing. More formally, $A[i, j] ≤ A[i, j + 1]$ for every $i = 1, 2, \ldots , n$ and $j = 1, 2, \ldots , m−1$, and $A[i, j] \leq A[i+1, j]$ for every $i = 1, 2, \ldots , n−1$ and $j = 1,2, \ldots ,m$.
Describe and analyze an algorithm with running time $O(nm \lg m)$ that produces a sorted $1$~$D$ array of length nm that has exactly the same elements as $A$. For example, for the $2D$ array given above, the algorithm should return $[1, 2, 3, 4, 4, 5, 5, 6, 8, 10, 19, 30]$.
The solution to this problem is to use the merge routine from merge-sort, along with a min-heap or an AVL tree.
This works by maintaining m pointers, one for each column of the $2D$ input array. The pointers start at the top of each column and we put each value of where the pointers point to into a min-heap (thus, the min-heap will at any given time contain at most m elements). We then use the merge routine from merge-sort to combine all the columns into a $1D$ array. The min-heap tells the merge routine which item to put next into the resulting array. When extracting an element from the heap, the pointer of the column of that element is advanced one element "down" and the corresponding value is inserted into the min-heap. This process is being performed until all the elements are processed.
I came up with a slightly different solution and was wondering if the time complexity still turns out to be $O(nm \lg m)$.
My solution works as follows (essentially a modified version of merge-sort):
- Start with the entire array
- Split the array in $2$ halfes by performing a column split (each of size $n \times m/2$)
- Recursively split the subproblems into halfes until we reach the base case
- Base case: the problem has size $n \times 1$ (a single column of the input array) and we simply return it (a $1D$ array that is already sorted)
- For every other step than the base case, merge the two $1D$ arrays into a $1D$ array that contains all the elements of the two columns but now sorted
Concerning the time complexity, my thoughts are:
- We split each subproblem into half the number of columns, thus resulting into $O(\log n)$ splits.
- At each level of the recursion we perform $O(nm)$ work (for merging).
Combining the two parts, this gives: $O(nm \lg m)$. Is this correct or am I missing something here?
My attempt:
$T(m,n) = 2T(\frac{m}{2}, n) + O(nm)$.
If I guess $O(nm \lg m)$ as a solution to this recurrence $\forall k$, $k < m$, this is what I get by induction:
\begin{align} T(m, n) &= 2T(\frac{m}{2}, n) + O(nm) \\ &= 2T(\frac{m}{2}, n) + c \cdot nm \\ &= 2(c \cdot n\frac{m}{2} \lg\frac{m}{2}) + c \cdot nm \\ &= c \cdot nm (\lg m - \lg 2) + c \cdot nm \\ &= c \cdot nm \lg m - c \cdot nm + c \cdot nm \\ &= c \cdot nm \lg m \end{align}
As far as I can see, this is the desired complexity but I still feel like I am missing something here.