Skip to content

Commit

Permalink
Add problem 1632: Rank Transform of a Matrix
Browse files Browse the repository at this point in the history
  • Loading branch information
EFanZh committed Oct 4, 2023
1 parent 1017f38 commit eb4bfad
Show file tree
Hide file tree
Showing 3 changed files with 208 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1342,6 +1342,7 @@ pub mod problem_1627_graph_connectivity_with_threshold;
pub mod problem_1629_slowest_key;
pub mod problem_1630_arithmetic_subarrays;
pub mod problem_1631_path_with_minimum_effort;
pub mod problem_1632_rank_transform_of_a_matrix;
pub mod problem_1636_sort_array_by_increasing_frequency;
pub mod problem_1637_widest_vertical_area_between_two_points_containing_no_points;
pub mod problem_1640_check_array_formation_through_concatenation;
Expand Down
66 changes: 66 additions & 0 deletions src/problem_1632_rank_transform_of_a_matrix/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
pub mod union_find;

pub trait Solution {
fn matrix_rank_transform(matrix: Vec<Vec<i32>>) -> Vec<Vec<i32>>;
}

#[cfg(test)]
mod tests {
use super::Solution;
use crate::test_utilities::Matrix;

pub fn run<S: Solution>() {
let test_cases = [
(&[[1, 2], [3, 4]] as &dyn Matrix<_>, &[[1, 2], [2, 3]] as &dyn Matrix<_>),
(&[[7, 7], [7, 7]], &[[1, 1], [1, 1]]),
(
&[[20, -21, 14], [-19, 4, 19], [22, -47, 24], [-19, 4, 19]],
&[[4, 2, 3], [1, 3, 4], [5, 1, 6], [1, 3, 4]],
),
(
&[
[25, 8, 31, 42, -39, 8, 31, -10, 33, -44, 7, -30, 9, 44, 15, 26],
[-3, -48, -17, -18, 9, -12, -21, 10, 1, 44, -17, 14, -27, 48, -21, -6],
[49, 28, 27, -18, -31, 4, -13, 34, 49, 48, 47, -18, 33, 40, 15, 38],
[5, -28, -49, -38, 1, 32, -25, -50, 29, -32, 35, -46, -43, 48, -49, -6],
[-27, -24, 23, -14, -47, -12, 7, 6, 25, -16, 47, -26, 13, -12, -33, -18],
[45, -48, 3, -26, -23, -36, -17, 38, 17, 12, 15, 46, 37, 40, 47, 26],
[-19, -24, -21, -2, -7, -48, 47, 30, 5, -8, 23, -46, 21, -32, -33, -26],
[-27, 32, 27, -26, 21, -32, -49, -10, 5, 20, -29, 46, -43, -44, 39, 22],
[-43, 48, 27, 26, -27, 12, -1, -10, -27, 12, -29, -34, 41, -28, -25, -30],
[25, -36, 35, -26, 37, -20, 31, 14, -19, -40, -29, -2, -39, -28, 11, 46],
[49, -32, -29, -6, -47, 32, -17, -18, -23, 24, 23, 22, -47, -44, 27, 14],
[
37, -44, -33, -18, -47, 24, -17, -46, -43, -32, 15, -46, -27, -8, -25, 46,
],
[41, -40, 31, -30, 13, -24, -29, 22, -15, -16, 47, 2, -39, 4, -25, -42],
[-3, 12, 7, 14, -7, 8, -37, -34, -7, -12, 39, -38, 1, 44, 27, -34],
[-47, 4, 7, -2, -43, -32, 27, 2, -43, -8, -33, 14, 49, -48, -5, 30],
[-15, 8, -33, -26, -23, -32, -25, 22, 13, -20, -9, 26, 29, 4, -1, 2],
],
&[
[25, 22, 28, 30, 6, 22, 28, 13, 29, 1, 16, 7, 23, 33, 24, 27],
[18, 1, 14, 13, 20, 16, 12, 21, 19, 28, 14, 22, 8, 34, 12, 17],
[35, 28, 27, 13, 7, 17, 15, 30, 35, 34, 33, 13, 29, 32, 24, 31],
[19, 9, 2, 5, 18, 29, 11, 1, 27, 7, 30, 3, 4, 34, 2, 17],
[9, 11, 25, 15, 1, 16, 21, 20, 26, 14, 33, 10, 24, 16, 4, 12],
[33, 1, 15, 10, 12, 2, 14, 31, 25, 23, 24, 34, 30, 32, 35, 27],
[13, 11, 12, 18, 17, 1, 29, 27, 20, 16, 26, 3, 25, 5, 4, 8],
[9, 29, 27, 10, 25, 7, 1, 13, 20, 24, 8, 34, 4, 2, 30, 26],
[3, 32, 27, 25, 10, 23, 16, 13, 10, 23, 8, 5, 31, 9, 11, 7],
[25, 7, 29, 10, 30, 13, 28, 22, 14, 2, 8, 15, 6, 9, 19, 32],
[35, 8, 9, 16, 1, 29, 14, 12, 11, 27, 26, 23, 1, 2, 28, 20],
[26, 4, 6, 13, 1, 25, 14, 3, 5, 7, 24, 3, 8, 17, 11, 32],
[29, 5, 28, 7, 21, 12, 8, 24, 15, 14, 33, 16, 6, 20, 11, 1],
[18, 23, 21, 24, 17, 22, 5, 6, 17, 15, 31, 4, 19, 33, 28, 6],
[2, 20, 21, 18, 5, 7, 23, 19, 5, 16, 6, 22, 32, 1, 17, 28],
[14, 22, 6, 10, 12, 7, 11, 24, 23, 13, 15, 25, 26, 20, 18, 19],
],
),
];

for (matrix, expected) in test_cases {
assert_eq!(S::matrix_rank_transform(matrix.to_vec()), expected);
}
}
}
141 changes: 141 additions & 0 deletions src/problem_1632_rank_transform_of_a_matrix/union_find.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
pub struct Solution;

// ------------------------------------------------------ snip ------------------------------------------------------ //

use std::collections::HashMap;
use std::iter::FromIterator;

// See <https://leetcode.com/problems/rank-transform-of-a-matrix/discuss/1391380/C%2B%2BPython-HashMap-and-Sorting-and-Union-Find-Clean-and-Concise>.

impl Solution {
fn find_root(union_find: &mut [(u16, u16)], node: u16) -> (u16, u16) {
let state = union_find[usize::from(node)];

if state.0 == 0 {
(node, state.1)
} else {
let result = Self::find_root(union_find, state.0 - 1);

if state.0 != result.0 + 1 {
union_find[usize::from(node)].0 = result.0 + 1;
}

result
}
}

fn union(union_find: &mut [(u16, u16)], left: u16, right: u16) {
let left_root = Self::find_root(union_find, left);
let right_root = Self::find_root(union_find, right);

if left_root.0 != right_root.0 {
let (child, parent) = if left_root.1 < right_root.1 {
(left_root.0, right_root.0)
} else {
if left_root.1 == right_root.1 {
union_find[usize::from(left_root.0)].1 += 1;
}

(right_root.0, left_root.0)
};

union_find[usize::from(child)].0 = parent + 1;
}
}

pub fn matrix_rank_transform(matrix: Vec<Vec<i32>>) -> Vec<Vec<i32>> {
let mut matrix = matrix;
let rows = matrix.len();
let columns = matrix.first().map_or(0, Vec::len);

// Group cells by values.

let mut cells_by_values = HashMap::<_, Vec<(u16, u16)>>::new();

for (y, row) in (0..).zip(&matrix) {
for (x, &value) in (0..).zip(row) {
cells_by_values
.entry(value)
.and_modify(|coordinates| coordinates.push((y, x)))
.or_insert_with(|| vec![(y, x)]);
}
}

// Sort cells by values.

let mut sorted_cells = Vec::from_iter(cells_by_values);

sorted_cells.sort_unstable_by_key(|&(key, _)| key);

// Assign ranks.

let column_start = rows as u16;
let line_count = rows + columns;
let mut ranks = vec![0; line_count].into_boxed_slice();
let mut union_find = vec![(0, 0); line_count].into_boxed_slice();
let mut max_ranks = vec![0; line_count].into_boxed_slice();

for (_, cells) in sorted_cells {
// Group lines.

for &(y, x) in &cells {
Self::union(&mut union_find, y, column_start + x);
}

// Find maximum rank of each group.

for &(y, x) in &cells {
let max_rank = &mut max_ranks[usize::from(Self::find_root(&mut union_find, y).0)];

for line in [y, column_start + x] {
*max_rank = (*max_rank).max(ranks[usize::from(line)]);
}
}

// Assign new ranks.

for &(y, x) in &cells {
let new_rank = max_ranks[usize::from(Self::find_root(&mut union_find, y).0)] + 1;

for line in [y, column_start + x] {
ranks[usize::from(line)] = new_rank;
}
}

// Update matrix.

for &(y, x) in &cells {
matrix[usize::from(y)][usize::from(x)] = ranks[usize::from(y)];
}

// Reset caches.

for &(y, x) in &cells {
for line in [y, column_start + x] {
let line = usize::from(line);

union_find[line] = (0, 0);
max_ranks[line] = 0;
}
}
}

matrix
}
}

// ------------------------------------------------------ snip ------------------------------------------------------ //

impl super::Solution for Solution {
fn matrix_rank_transform(matrix: Vec<Vec<i32>>) -> Vec<Vec<i32>> {
Self::matrix_rank_transform(matrix)
}
}

#[cfg(test)]
mod tests {
#[test]
fn test_solution() {
super::super::tests::run::<super::Solution>();
}
}

0 comments on commit eb4bfad

Please sign in to comment.