Sorting a square matrix based on its diagonals is an interesting problem that tests our understanding of array manipulation and sorting techniques.
Understanding the Problem in Simple Terms
We are given an n × n square matrix, and we need to sort its diagonals according to the following rules:
- Diagonals in the bottom-left triangle (including the middle diagonal) should be sorted in non-increasing order (descending).
- Diagonals in the top-right triangle should be sorted in non-decreasing order (ascending).
Example Walkthrough
Example 1
Input:
Output:
Example 2
Input:
Output:
Brute Force Approach – Sorting Each Diagonal Individually
Step-by-Step Approach
- Extract all diagonals from the matrix.
- Sort each diagonal according to the required order.
- Place the sorted diagonals back into the matrix.
Time Complexity:
- Extracting diagonals: O(n²)
- Sorting each diagonal: O(n log n)
- Placing them back: O(n²)
- Total Complexity: O(n² log n)
C++ Implementation
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
class Solution
{
public:
vector<vector<int>> sortMatrix(vector<vector<int>> &grid)
{
int n = grid.size();
// Sort all diagonals separately
for (int d = -(n - 1); d <= (n - 1); d++)
{
vector<int> diag;
// Collect diagonal elements
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (i - j == d)
{
diag.push_back(grid[i][j]);
}
}
}
// Sort the diagonal accordingly
if (d >= 0)
sort(diag.begin(), diag.end(), greater<int>()); // Descending order
else
sort(diag.begin(), diag.end()); // Ascending order
// Put sorted elements back
int index = 0;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (i - j == d)
{
grid[i][j] = diag[index++];
}
}
}
}
return grid;
}
};
int main()
{
Solution sol;
vector<vector<int>> grid = {{1, 7, 3}, {9, 8, 2}, {4, 5, 6}};
vector<vector<int>> sortedGrid = sol.sortMatrix(grid);
for (auto row : sortedGrid)
{
for (int num : row)
{
cout << num << " ";
}
cout << endl;
}
return 0;
}
Drawbacks of the Brute Force Approach
- We are iterating over all elements multiple times.
- Sorting each diagonal separately increases the runtime.
Optimized Approach – Using Hash Map for Efficient Sorting
Instead of checking every element manually, we can use a hash map where:
- Key =
(i - j)
(which uniquely identifies each diagonal) - Value = List of elements in that diagonal
Steps to Optimize
- Store all diagonal elements in a hash map.
- Sort each diagonal efficiently (descending for bottom-left, ascending for top-right).
- Reconstruct the matrix using sorted diagonals.
Optimized Code Implementation(C++)
#include <vector>
#include <algorithm>
#include <unordered_map>
using namespace std;
class Solution
{
public:
vector<vector<int>> sortMatrix(vector<vector<int>> &grid)
{
int n = grid.size();
unordered_map<int, vector<int>> diagMap;
// Collect all diagonals
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
diagMap[i - j].push_back(grid[i][j]);
}
}
// Sort diagonals based on their positions
for (auto &[key, values] : diagMap)
{
if (key >= 0)
{
sort(values.begin(), values.end(), greater<int>()); // Descending for bottom-left
}
else
{
sort(values.begin(), values.end()); // Ascending for top-right
}
}
// Fill sorted values back into the matrix
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
grid[i][j] = diagMap[i - j].back();
diagMap[i - j].pop_back();
}
}
return grid;
}
};
Time Complexity of Optimized Solution
- Collecting diagonals: O(n²)
- Sorting diagonals: O(n log n)
- Reconstructing matrix: O(n²)
- Total Complexity: O(n² log n), but it reduces redundant computations.
- We explored two solutions: a simple brute force approach and an optimized hash map approach.
- The optimized method reduces unnecessary operations and sorts diagonals efficiently.
- Understanding how to identify diagonals using
(i - j)
is the key to solving this problem effectively.
(i - j)
is the key to solving this problem effectively.FAQs
1. Can we solve this problem in O(n²) time?
Not exactly, since sorting takes O(n log n). However, using bucket sort (if constraints allow) could make sorting nearly O(n).
2. Why did we use an unordered_map?
It helps in efficiently storing and retrieving diagonal elements using
(i - j)
as a unique key.