mirror of
https://github.com/mfocko/blog.git
synced 2025-05-10 13:22:57 +02:00
blog(leetcode): add matrix sort
Signed-off-by: Matej Focko <mfocko@redhat.com>
This commit is contained in:
parent
d6644d74ab
commit
7ed3cd3298
3 changed files with 919 additions and 0 deletions
static/files/blog/leetcode/sort-matrix-diagonally
16
static/files/blog/leetcode/sort-matrix-diagonally/makefile
Normal file
16
static/files/blog/leetcode/sort-matrix-diagonally/makefile
Normal file
|
@ -0,0 +1,16 @@
|
|||
CXX=clang++
|
||||
CXXFLAGS=-std=c++20 -Wall -Wextra -Werror -g -pedantic
|
||||
|
||||
test: matrix-sort
|
||||
./matrix-sort
|
||||
|
||||
matrix-sort: format tidy
|
||||
$(CXX) $(CXXFLAGS) matrix-sort.cpp -o matrix-sort
|
||||
|
||||
format:
|
||||
clang-format -i -style=webkit *.cpp
|
||||
|
||||
tidy:
|
||||
clang-tidy *.cpp -- $(CXXFLAGS)
|
||||
|
||||
.PHONY: matrix-sort format tidy
|
|
@ -0,0 +1,229 @@
|
|||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
|
||||
using matrix = std::vector<std::vector<int>>;
|
||||
|
||||
template <typename T>
|
||||
class diagonal {
|
||||
using matrix_t = std::vector<std::vector<T>>;
|
||||
|
||||
matrix_t& matrix;
|
||||
std::size_t x;
|
||||
std::size_t y;
|
||||
|
||||
class diagonal_iter {
|
||||
// we need to keep reference to the matrix itself
|
||||
std::reference_wrapper<matrix_t> m;
|
||||
|
||||
// we need to be able to tell our current position
|
||||
std::size_t x;
|
||||
std::size_t y;
|
||||
|
||||
public:
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = T;
|
||||
using pointer = T*;
|
||||
using reference = T&;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
|
||||
diagonal_iter(matrix_t& matrix,
|
||||
std::size_t x,
|
||||
std::size_t y)
|
||||
: m(matrix)
|
||||
, x(x)
|
||||
, y(y)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator==(const diagonal_iter& rhs) const
|
||||
{
|
||||
return x == rhs.x && y == rhs.y && m.get() == rhs.m.get();
|
||||
}
|
||||
|
||||
diagonal_iter& operator++()
|
||||
{
|
||||
// we are moving along the diagonal, so we increment both ‹x› and ‹y›
|
||||
// at the same time
|
||||
x++;
|
||||
y++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
reference operator*() const { return m.get()[y][x]; }
|
||||
|
||||
// exactly opposite to the incrementation
|
||||
diagonal_iter operator--()
|
||||
{
|
||||
x--;
|
||||
y--;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// moving ‹n› steps back is same as calling decrementation ‹n›-times, so
|
||||
// we can just return a new iterator and subtract ‹n› from both
|
||||
// coordinates in the matrix
|
||||
diagonal_iter operator-(difference_type n) const
|
||||
{
|
||||
return diagonal_iter { m, x - n, y - n };
|
||||
}
|
||||
|
||||
// here we assume that we are given two iterators on the same diagonal
|
||||
difference_type operator-(const diagonal_iter& rhs) const
|
||||
{
|
||||
assert(m.get() == rhs.m.get());
|
||||
return x - rhs.x;
|
||||
}
|
||||
|
||||
// counterpart of moving ‹n› steps backwards
|
||||
diagonal_iter operator+(difference_type n) const
|
||||
{
|
||||
return diagonal_iter { m, x + n, y + n };
|
||||
}
|
||||
|
||||
// we compare the coordinates, and also assume that those 2 iterators are
|
||||
// lying on the same diagonal
|
||||
bool operator<(const diagonal_iter& rhs) const
|
||||
{
|
||||
assert(m.get() == rhs.m.get());
|
||||
return x < rhs.x && y < rhs.y;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
diagonal(matrix_t& matrix, std::size_t x, std::size_t y)
|
||||
: matrix(matrix)
|
||||
, x(x)
|
||||
, y(y)
|
||||
{
|
||||
}
|
||||
|
||||
diagonal_iter begin() const { return diagonal_iter { matrix, x, y }; }
|
||||
|
||||
diagonal_iter end() const
|
||||
{
|
||||
auto max_x = matrix[y].size();
|
||||
auto max_y = matrix.size();
|
||||
|
||||
// we need to find the distance in which we get out of bounds (either in
|
||||
// column or row)
|
||||
auto steps = std::min(max_x - x, max_y - y);
|
||||
|
||||
return diagonal_iter { matrix, x + steps, y + steps };
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class diagonals {
|
||||
using matrix_t = std::vector<std::vector<T>>;
|
||||
|
||||
class diagonals_iter {
|
||||
matrix_t& m;
|
||||
std::size_t x;
|
||||
std::size_t y;
|
||||
|
||||
public:
|
||||
diagonals_iter(matrix_t& matrix, std::size_t x, std::size_t y)
|
||||
: m(matrix)
|
||||
, x(x)
|
||||
, y(y)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator!=(const diagonals_iter& rhs) const
|
||||
{
|
||||
// iterators are not equal if they reference different matrices, or
|
||||
// their positions differ
|
||||
return x != rhs.x || y != rhs.y || m != rhs.m;
|
||||
}
|
||||
|
||||
diagonals_iter& operator++()
|
||||
{
|
||||
if (y != 0) {
|
||||
// iterating through diagonals down the first column
|
||||
y++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// iterating the diagonals along the first row
|
||||
x++;
|
||||
if (x == m.front().size()) {
|
||||
// switching to diagonals in the first column
|
||||
x = 0;
|
||||
y++;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
diagonal<T> operator*() const { return diagonal { m, x, y }; }
|
||||
};
|
||||
|
||||
matrix_t& _matrix;
|
||||
|
||||
public:
|
||||
diagonals(matrix_t& m)
|
||||
: _matrix(m)
|
||||
{
|
||||
}
|
||||
diagonals_iter begin() { return diagonals_iter { _matrix, 0, 0 }; }
|
||||
diagonals_iter end() { return diagonals_iter { _matrix, 0, _matrix.size() }; }
|
||||
};
|
||||
|
||||
class Solution {
|
||||
public:
|
||||
matrix diagonalSort(matrix mat)
|
||||
{
|
||||
// we iterate over the diagonals
|
||||
for (auto d : diagonals(mat)) {
|
||||
// and we sort each diagonal
|
||||
std::sort(d.begin(), d.end());
|
||||
}
|
||||
|
||||
// we take the matrix by copy, so we can sort in-situ and return the copy
|
||||
// that we sorted
|
||||
return mat;
|
||||
}
|
||||
};
|
||||
|
||||
static void test_case_1()
|
||||
{
|
||||
// Input: mat = [[3,3,1,1],[2,2,1,2],[1,1,1,2]]
|
||||
// Output: [[1,1,1,1],[1,2,2,2],[1,2,3,3]]
|
||||
|
||||
Solution s;
|
||||
assert((s.diagonalSort(std::vector { std::vector { 3, 3, 1, 1 },
|
||||
std::vector { 2, 2, 1, 2 },
|
||||
std::vector { 1, 1, 1, 2 } })
|
||||
== std::vector { std::vector { 1, 1, 1, 1 },
|
||||
std::vector { 1, 2, 2, 2 },
|
||||
std::vector { 1, 2, 3, 3 } }));
|
||||
}
|
||||
|
||||
static void test_case_2()
|
||||
{
|
||||
// Input: mat =
|
||||
// [[11,25,66,1,69,7],[23,55,17,45,15,52],[75,31,36,44,58,8],[22,27,33,25,68,4],[84,28,14,11,5,50]]
|
||||
// Output:
|
||||
// [[5,17,4,1,52,7],[11,11,25,45,8,69],[14,23,25,44,58,15],[22,27,31,36,50,66],[84,28,75,33,55,68]]
|
||||
|
||||
Solution s;
|
||||
assert((s.diagonalSort(std::vector { std::vector { 11, 25, 66, 1, 69, 7 },
|
||||
std::vector { 23, 55, 17, 45, 15, 52 },
|
||||
std::vector { 75, 31, 36, 44, 58, 8 },
|
||||
std::vector { 22, 27, 33, 25, 68, 4 },
|
||||
std::vector { 84, 28, 14, 11, 5, 50 } })
|
||||
== std::vector { std::vector { 5, 17, 4, 1, 52, 7 },
|
||||
std::vector { 11, 11, 25, 45, 8, 69 },
|
||||
std::vector { 14, 23, 25, 44, 58, 15 },
|
||||
std::vector { 22, 27, 31, 36, 50, 66 },
|
||||
std::vector { 84, 28, 75, 33, 55, 68 } }));
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_case_1();
|
||||
test_case_2();
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue