
Given a matrix of M x N elements, return the elements in diagonal order as shown below.
[ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ], [13, 14, 15, 16 ] ]
Output: [1, 2, 5, 9, 6, 3, 4, 7, 10, 13, 14, 11, 8, 12, 15, 16]

Solution #1: To begin with, let’s ignore the part where alternate diagonal elements need to be reversed. Let’s try to traverse the matrix as shown above in image 2.
We see that a diagonal always starts either from the top row or the last column of elements. If we are given the starting point of a diagonal, how can we get to the next diagonal element?
For example, let’s say we are at 4 which is at (r = 0, c = 3) where r and c indicate the row and column indexes respectively. To get to the next element, we have to move one row down and one column to the left, ie., r = 1, c = 2. To generalize:
next_r = r + 1, next_c = c – 1

To match the expected results where alternate diagonal elements are reversed in order, we can collect the diagonal elements and reverse the order of alternate diagonals before adding to the results. The code below is an intuitive way of solving this problem.
def findDiagonalOrder(self, matrix: List[List[int]]) -> List[int]:
if not matrix or not matrix[0]:
return []
m, n = len(matrix), len(matrix[0])
results = []
row, col = 0, 0
while row < m and col < n:
temp = []
# To collect all elements of a diagonal, keep going one row down and column
# to the left until we hit matrix boundaries
i, j = row, col
while i < m and j >= 0:
temp.append(matrix[i][j])
i += 1
j -= 1
# Reverse the order for alternate diagonals
if (row + col) % 2 == 0:
results.extend(temp[::-1])
else:
results.extend(temp)
# Set row, col to traverse the next diagonal
if col < n - 1:
col += 1
else:
row += 1
return results
Time Complexity: O(M*N) where M is the number of rows and N is the number of columns in the matrix
Space Complexity: The additional space used in this solution is the temp array which holds the diagonal elements. The largest diagonal of a matrix has min(M, N) elements, so space complexity is O(min(M, N)).
Testing: When it comes to matrix problems, make sure to test the following cases.
- Empty matrix: []
- Empty rows: [[]]
- Single value matrix: [[1]]
- Two element matrix: [[1, 2]]
- Column matrix: [[1,2,3,4]]
- Row matrix: [[1],[2],[3],[4]]
- Symmetric matrix: [[1,2,3, 4],[4,5,6, 8], [9,10,11,12],[13,14,15,16]]
- Asymmetric matrix: [[1,2,3, 4],[4,5,6, 8]]
Solution #2: The above solution is perfectly acceptable. If you notice the indices of diagonal elements, they all sum to the same number. Remember this pattern as it might come handy while manipulating diagonal elements of a matrix.

Keeping this in mind, here is an alternate solution to this problem where we use the sum of indices as keys in a dictionary to store values belonging to a particular diagonal.
import collections
def findDiagonalOrder(self, matrix: List[List[int]]) -> List[int]:
if not matrix or not matrix[0]:
return []
m, n = len(matrix), len(matrix[0])
elements = collections.defaultdict(list)
results = []
for i in range(m):
for j in range(n):
elements[i+j].append(matrix[i][j])
for k, v in elements.items():
if k % 2 == 0:
results.extend(v[::-1])
else:
results.extend(v)
return results
Time Complexity: O(M*N)
Space Complexity: O(M*N)
Want to try solving this problem? https://leetcode.com/problems/diagonal-traverse/
Hope you enjoyed solving the diagonal traverse problem. Have a great day!
