【每日一题Day98】LCLC1632矩阵转换后的秩 | TreeMap+并查集

矩阵转换后的秩【LC1632】

给你一个 m x n 的矩阵 matrix ,请你返回一个新的矩阵 answer ,其中 answer[row][col]matrix[row][col] 的秩。

每个元素的 是一个整数,表示这个元素相对于其他元素的大小关系,它按照如下规则计算:

  • 秩是从 1 开始的一个整数。
  • 如果两个元素 p 和 q在同一行或者同一列,那么:
    • 如果 p < q ,那么 rank(p) < rank(q)
    • 如果 p == q ,那么 rank(p) == rank(q)
    • 如果 p > q ,那么 rank(p) > rank(q)
  • 需要越 越好。

题目保证按照上面规则 answer 数组是唯一的。

感觉思路挺对 结果改了好久 有些案例跟答案差1差2的 改完一个又来一个 累了 明天再改了 =(
怎么也想不到是并查集…

  • 思路:

    • 将矩阵组成三元组{值,横坐标,纵坐标},根据值从小到大排序,按照顺序赋予秩
      • 如果当前值在同行或者同列中未出现过,那么秩为该行该列秩的最大值+1,
      • 如果当前值在同行或者同列中出现过,那么这些位置的秩为这些行列中秩的最大值+1,因此需要统计每行每列秩的最大值
  • 实现【并查集】

    • 使用TreeMap存放某个值的所有下标,key值为数值,value为动态数组存放下标,key值从小到大排序
    • 然后遍历TreeMap,使用并查集处理存在相同元素时的情况,将元素分为几个连通块,对于每个连通块,里面所有元素对应的秩为这些行或列的最大秩加 1。
      • 首先将某个数值的所有row和col加入并查集中,列值对应的下标+m【将二维化为一维】
      • 然后对于每一个连通块找到所有节点秩的最大值,记录在rank数组中,下标为该连通块的根节点序号
      • 再对该连通块内每一个位置赋予结果为最大秩+1
      • 最后对并查集复原
      • 并查集中size属性不是必须的,可以去除
    // 并查集类
    class UnionFind {
        private int[] p;// 值
        private int[] size;// 每个节点的子节点个数(包括自身) 非必须 可去除
    	// 初始化
        public UnionFind(int n) {
            p = new int[n];
            size = new int[n];
            for (int i = 0; i < n; ++i) {
                p[i] = i;
                size[i] = 1;
            }
        }
    	// 找到节点x的根
        public int find(int x) {
            if (p[x] != x) {
                p[x] = find(p[x]);
            }
            return p[x];
        }
    	// 在并查集中加入a->b的根
        public void union(int a, int b) {
            int pa = find(a), pb = find(b);
            if (pa != pb) {
                if (size[pa] > size[pb]) {
                    p[pb] = pa;
                    size[pa] += size[pb];
                } else {
                    p[pa] = pb;
                    size[pb] += size[pa];
                }
            }
        }
    	// 重置并查集
        public void reset(int x) {
            p[x] = x;
            size[x] = 1;
        }
    }
    
    
    class Solution {
        public int[][] matrixRankTransform(int[][] matrix) {
            int m = matrix.length, n = matrix[0].length;
            // 存放值对应的下标
            TreeMap<Integer, List<int[]>> d = new TreeMap<>();
            for (int i = 0; i < m; ++i) {
                for (int j = 0; j < n; ++j) {
                    d.computeIfAbsent(matrix[i][j], k -> new ArrayList<>()).add(new int[] {i, j});
                }
            }
            int[] rowMax = new int[m];
            int[] colMax = new int[n];
            int[][] ans = new int[m][n];
            // 构造并查集
            UnionFind uf = new UnionFind(m + n);
            int[] rank = new int[m + n];
            // 从小到大遍历某个值的所有下标
            for (var ps : d.values()) {
                // ps:坐标 p[0]:row p[1]:col p[1]+m:newCol【二维化一维】
                // 将row->col+m这条边加入并查集
                // 如果有以下三个位置的数值相同[row1,col1][row1,col2][row2,col2][row3,col3]
                // 那么节点row1,row2,col1+m,col2+m会有同一个根节点
                // 节点row3,col3会有同一个根节点
                for (var p : ps) {
                    uf.union(p[0], p[1] + m);
                }
                // 对于每个连通块,找到里面所有元素对应的行或列的最大秩
                for (var p : ps) {
                    int i = p[0], j = p[1];
                    rank[uf.find(i)] = Math.max(rank[uf.find(i)], Math.max(rowMax[i], colMax[j]));
                }
                // 对于每个连通块,其秩为最大秩+1
                for (var p : ps) {
                    int i = p[0], j = p[1];
                    ans[i][j] = 1 + rank[uf.find(i)];
                    rowMax[i] = ans[i][j];
                    colMax[j] = ans[i][j];
                }
                // 处理完一个连通块之后需要将并查集复原
                for (var p : ps) {
                    uf.reset(p[0]);
                    uf.reset(p[1] + m);
                }
            }
            return ans;
        }
    }
    
    作者:ylb
    链接:https://leetcode.cn/problems/rank-transform-of-a-matrix/solutions/2075493/by-lcbin-ll29/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    
    • 复杂度
      • 时间复杂度: O ( m × n × ( l o g ⁡ m + l o g ⁡ n ) ) O(m×n×(log⁡m+log⁡n)) O(m×n×(logm+logn))。并查集的合并次数是 O ( m × n ) O(m×n) O(m×n),构建TreeMap的复杂度为 O ( m × n × ( l o g ⁡ m + l o g ⁡ n ) ) O(m×n×(log⁡m+log⁡n)) O(m×n×(logm+logn))
      • 空间复杂度: O ( m × n ) O(m×n) O(m×n)。并查集的空间复杂度为 O ( m + n ) O(m+n) O(m+n),TreeMap的空间复杂度为 O ( m × n ) O(m×n) O(m×n)

你可能感兴趣的:(每日一题,排序,并查集,算法)