Back to all solutions
#1632 - Rank Transform of a Matrix
Problem Description
Given an m x n matrix, return a new matrix answer where answer[row][col] is the rank of matrix[row][col].
The rank is an integer that represents how large an element is compared to other elements.
It is calculated using the following rules:
- The rank is an integer starting from 1.
- If two elements p and q are in the same row or column, then:
- If p < q then rank(p) < rank(q)
- If p == q then rank(p) == rank(q)
- If p > q then rank(p) > rank(q)
- The rank should be as small as possible.
The test cases are generated so that answer is unique under the given rules.
Solution
/**
* @param {number[][]} matrix
* @return {number[][]}
*/
var matrixRankTransform = function(matrix) {
const rows = matrix.length;
const cols = matrix[0].length;
const result = Array.from({ length: rows }, () => new Array(cols).fill(0));
const parent = new Array(rows * cols).fill(-1);
const elements = [];
for (let row = 0; row < rows; row++) {
const valueToCoords = new Map();
for (let col = 0; col < cols; col++) {
const value = matrix[row][col];
if (!valueToCoords.has(value)) valueToCoords.set(value, []);
valueToCoords.get(value).push([row, col]);
}
for (const coords of valueToCoords.values()) {
for (let i = 1; i < coords.length; i++) {
const [r1, c1] = coords[0];
const [r2, c2] = coords[i];
union(r1 * cols + c1, r2 * cols + c2);
}
}
}
for (let col = 0; col < cols; col++) {
const valueToCoords = new Map();
for (let row = 0; row < rows; row++) {
const value = matrix[row][col];
if (!valueToCoords.has(value)) valueToCoords.set(value, []);
valueToCoords.get(value).push([row, col]);
}
for (const coords of valueToCoords.values()) {
for (let i = 1; i < coords.length; i++) {
const [r1, c1] = coords[0];
const [r2, c2] = coords[i];
union(r1 * cols + c1, r2 * cols + c2);
}
}
}
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
elements.push([matrix[row][col], row, col]);
}
}
elements.sort((a, b) => a[0] - b[0]);
const rowMaxRank = new Array(rows).fill(0);
const colMaxRank = new Array(cols).fill(0);
const groups = new Map();
for (let i = 0; i < elements.length; i++) {
const [value, row, col] = elements[i];
const root = find(row * cols + col);
if (!groups.has(root)) groups.set(root, []);
groups.get(root).push([row, col]);
}
let i = 0;
while (i < elements.length) {
const value = elements[i][0];
const currentGroups = new Map();
while (i < elements.length && elements[i][0] === value) {
const [, row, col] = elements[i];
const root = find(row * cols + col);
if (!currentGroups.has(root)) currentGroups.set(root, []);
currentGroups.get(root).push([row, col]);
i++;
}
for (const [root, coords] of currentGroups) {
let maxRank = 0;
for (const [row, col] of coords) {
maxRank = Math.max(maxRank, rowMaxRank[row], colMaxRank[col]);
}
const rank = maxRank + 1;
for (const [row, col] of coords) {
result[row][col] = rank;
rowMaxRank[row] = rank;
colMaxRank[col] = rank;
}
}
}
return result;
function find(x) {
if (parent[x] < 0) return x;
return parent[x] = find(parent[x]);
}
function union(x, y) {
const px = find(x);
const py = find(y);
if (px !== py) {
if (parent[px] < parent[py]) {
parent[px] += parent[py];
parent[py] = px;
} else {
parent[py] += parent[px];
parent[px] = py;
}
}
}
};