In the Leetcode problem Diagonal Traverse II, you are given a list of lists of integers called nums
, where each inner list represents a row in a 2D array. The 2D array is jagged, meaning that rows can have different lengths. Your task is to return all the elements of nums
in diagonal order as described below:
nums
must appear exactly once in the output.
Constraints:
1 <= nums.length <= 10^5
1 <= nums[i].length <= min(10^5, 10^5 - sum_{j=0}^{i-1} nums[j].length)
10^5
.The problem guarantees that there is only one valid output order.
To solve this problem, we need to understand how the diagonal traversal works for a jagged 2D array. In a regular matrix, the diagonal index for an element at (i, j)
is simply i + j
. All elements with the same i + j
lie on the same diagonal. However, since nums
can have rows of different lengths, we cannot assume a rectangular shape or use simple row/column iteration.
The brute-force approach would be to flatten the array and sort the elements by their diagonal index, but this would be inefficient. Instead, we look for a way to group elements by their diagonal index as we iterate through nums
. For each diagonal, we must process elements from bottom to top, which means as we collect elements, we need to reverse their order per diagonal before outputting them.
This leads us to consider using a hash map (dictionary) to group elements by diagonal, then outputting the diagonals in order. The key insight is that we can process each element once, group them efficiently, and then flatten the groups in the correct order.
We can break the solution into the following steps:
nums
:
For each element at position (i, j)
in nums
, compute the diagonal index as d = i + j
.
d
, and the value is a list of elements that fall on that diagonal. Since we want to output elements from bottom to top, we append elements as we find them, and later reverse each list.
This approach ensures that every element is visited exactly once, and that the output order matches the problem's requirements.
Let's walk through an example:
Input: nums = [ [1,2,3], [4,5,6], [7,8,9] ]
1
at (0, 0): diagonal = 0+0 = 0
2
at (0, 1): diagonal = 0+1 = 1
3
at (0, 2): diagonal = 0+2 = 2
4
at (1, 0): diagonal = 1+0 = 1
5
at (1, 1): diagonal = 1+1 = 2
6
at (1, 2): diagonal = 1+2 = 3
7
at (2, 0): diagonal = 2+0 = 2
8
at (2, 1): diagonal = 2+1 = 3
9
at (2, 2): diagonal = 2+2 = 4
Grouping by diagonal:
For each diagonal, reverse the list to get bottom-to-top order:
The final answer is: [1, 4, 2, 7, 5, 3, 8, 6, 9]
from collections import defaultdict
class Solution:
def findDiagonalOrder(self, nums):
diagonals = defaultdict(list)
for i, row in enumerate(nums):
for j, val in enumerate(row):
diagonals[i + j].append(val)
result = []
for d in sorted(diagonals.keys()):
# Reverse to get bottom-to-top order
result.extend(reversed(diagonals[d]))
return result
#include <vector>
#include <unordered_map>
#include <algorithm>
using namespace std;
class Solution {
public:
vector<int> findDiagonalOrder(vector<vector<int>>& nums) {
unordered_map<int, vector<int>> diagonals;
int maxKey = 0;
for (int i = 0; i < nums.size(); ++i) {
for (int j = 0; j < nums[i].size(); ++j) {
diagonals[i + j].push_back(nums[i][j]);
if (i + j > maxKey) maxKey = i + j;
}
}
vector<int> result;
for (int d = 0; d <= maxKey; ++d) {
if (diagonals.count(d)) {
auto& diag = diagonals[d];
result.insert(result.end(), diag.rbegin(), diag.rend());
}
}
return result;
}
};
import java.util.*;
class Solution {
public int[] findDiagonalOrder(List<List<Integer>> nums) {
Map<Integer, List<Integer>> diagonals = new HashMap<>();
int total = 0, maxKey = 0;
for (int i = 0; i < nums.size(); ++i) {
for (int j = 0; j < nums.get(i).size(); ++j) {
int idx = i + j;
diagonals.computeIfAbsent(idx, k -> new ArrayList<>()).add(nums.get(i).get(j));
if (idx > maxKey) maxKey = idx;
total++;
}
}
int[] result = new int[total];
int pos = 0;
for (int d = 0; d <= maxKey; ++d) {
if (diagonals.containsKey(d)) {
List<Integer> diag = diagonals.get(d);
for (int k = diag.size() - 1; k >= 0; --k) {
result[pos++] = diag.get(k);
}
}
}
return result;
}
}
var findDiagonalOrder = function(nums) {
const diagonals = new Map();
let maxKey = 0;
for (let i = 0; i < nums.length; ++i) {
for (let j = 0; j < nums[i].length; ++j) {
const idx = i + j;
if (!diagonals.has(idx)) diagonals.set(idx, []);
diagonals.get(idx).push(nums[i][j]);
if (idx > maxKey) maxKey = idx;
}
}
const result = [];
for (let d = 0; d <= maxKey; ++d) {
if (diagonals.has(d)) {
const diag = diagonals.get(d);
for (let k = diag.length - 1; k >= 0; --k) {
result.push(diag[k]);
}
}
}
return result;
};
Brute-force approach:
This is optimal since every element must be output exactly once.
The diagonal traversal of a jagged 2D array can be solved efficiently by grouping elements by their diagonal index (i + j
), and then processing each group in reverse order to satisfy the "bottom-to-top" requirement. This approach leverages a hash map for efficient grouping and ensures that the solution is both time and space optimal. The key insight is recognizing the diagonal grouping and the need to reverse the order within each group, making the solution both elegant and efficient.