zoukankan      html  css  js  c++  java
  • 算法

    矩阵问题

    问题一:转圈打印矩阵

    给定一个整型矩阵matrix,请按照转圈的方式打印它。

    要求:额外空间复杂度为O(1)。

    例如:
    [ 1  2  3  4 
      5  6  7  8 
      9 10 11 12 
     13 14 15 16 ]
        
    打印结果为:
    1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10
    

    本题可以在 LeetCode 54 螺旋矩阵测试结果。

    思路

    定义一个方法,以左上角的点和右下角的点开始,从边界开始打印矩形。打印一圈后循环遍历内圈的矩形,直到整个矩形打印完毕。有一些特殊的情况,比如打印的是一条竖线,或者横线,或者一个点,都没有关系,注意代码的边界即可。

    实现

    public static void spiralOrderPrint(int[][] matrix, int tR, int tC, int dR, int dC) {
        //三种情况:横线,竖线,矩形
        if (tR == dR) {
            for (int i = tC; i <= dC; i++) {
                System.out.printf("%3d", matrix[tR][i]);
            }
        } else if (tC == dC) {
            for (int i = tR; i <= dR; i++) {
                System.out.printf("%3d", matrix[i][tC]);
            }
        } else {
            int tmpC = tC;
            int tmpR = tR;
            while (tmpC < dC) {
                System.out.printf("%3d", matrix[tR][tmpC++]);
            }
            while (tmpR < dR) {
                System.out.printf("%3d", matrix[tmpR++][dC]);
            }
            while (tC < tmpC) {
                System.out.printf("%3d", matrix[dR][tmpC--]);
            }
            while (tR < tmpR) {
                System.out.printf("%3d", matrix[tmpR--][tC]);
            }
        }
    }
    
    public static void printMatrix(int[][] matrix) {
        System.out.println("-----------PrintMatrixInSpiralOrder.printMatrix-----------");
        int tR = 0;
        int tC = 0;
        int dR = matrix.length - 1;
        int dC = matrix[0].length - 1;
        //横线,竖线等特殊形式都囊括在内
        while (tR <= dR && tC <= dC) {
            spiralOrderPrint(matrix, tR++, tC++, dR--, dC--);
        }
    }
    

    测试

    public static void main(String[] args) {
        int[][] matrix = {
            {1, 2, 3, 4},
            {5, 6, 7, 8},
            {9, 10, 11, 12},
            {13, 14, 15, 16}
        };
    
        for (int[] arr : matrix) {
            for (int n : arr) {
                System.out.printf("%3d", n);
            }
            System.out.println();
        }
    
        printMatrix(matrix);
    }
    
    output:
      1  2  3  4
      5  6  7  8
      9 10 11 12
     13 14 15 16
    -----------PrintMatrixInSpiralOrder.printMatrix-----------
      1  2  3  4  8 12 16 15 14 13  9  5  6  7 11 10
    

    问题二:旋转正方形矩阵

    给定一个整型正方形矩阵 matrix,请把该矩阵调整成逆时针旋转 90 度的样子。

    要求:额外空间复杂度为O(1)。

    例如:
    [ 1  2  3
      4  5  6
      7  8  9 ]
    旋转:
    [ 3  6  9
      2  5  8
      1  4  7 ]
    

    思路

    先从外圈开始旋转,然后依次对内一圈一圈转,直到转完为止。

    实现

    public static void rotateMatrix(int[][] matrix) {
        int tR = 0;
        int tC = 0;
        int dR = matrix.length - 1;
        int dC = matrix[0].length - 1;
        //当两个点重合时,旋转就结束了
        while (tR < dR) {
            rotate(matrix, tR++, tC++, dR--, dC--);
        }
    }
    
    public static void rotate(int[][] matrix, int tR, int tC, int dR, int dC) {
        int times = dR - tR;
        for (int i = 0; i < times; i++) {
            //四个点变换位置
            int tmp = matrix[tR + i][tC];
            matrix[tR + i][tC] = matrix[tR][dC - i];
            matrix[tR][dC - i] = matrix[dR - i][dC];
            matrix[dR - i][dC] = matrix[dR][tC + i];
            matrix[dR][tC + i] = tmp;
        }
    }
    

    测试

    public static void main(String[] args) {
        //int[][] matrix = {
        //        {1, 2, 3},
        //        {4, 5, 6},
        //        {7, 8, 9},
        //};
    
        int[][] matrix = {
            {1, 2, 3, 4, 5},
            {6, 7, 8, 9, 10},
            {11, 12, 13, 14, 15},
            {16, 17, 18, 19, 20},
            {21, 22, 23, 24, 25}
        };
    
        printMatrix(matrix);
        rotateMatrix(matrix);
        printMatrix(matrix);
    
    }
    
    public static void printMatrix(int[][] matrix) {
        System.out.println("------------RotateMatrix.printMatrix------------");
        for (int[] arr : matrix) {
            for (int n : arr) {
                System.out.printf("%3d", n);
            }
            System.out.println();
        }
        System.out.println();
    }
    

    使用了两组数做测试,结果完成旋转,输出如下:

    ------------RotateMatrix.printMatrix------------
      1  2  3  4  5
      6  7  8  9 10
     11 12 13 14 15
     16 17 18 19 20
     21 22 23 24 25
    
    ------------RotateMatrix.printMatrix------------
      5 10 15 20 25
      4  9 14 19 24
      3  8 13 18 23
      2  7 12 17 22
      1  6 11 16 21
    

    小总结

    问题一和问题二都是矩阵的调整打印问题,采用的思路都很相似,从外围出发,一圈一圈向中心进攻。不要一直着眼在细节处,从宏观的角度浓缩出规律,再在细节处下功夫,如坐标的变换也是重点。这是一种比较通用的方法,把复杂的坐标变换浓缩成规律的活动,多训练这种思考方式,才能更便捷的解决问题。

    问题三:“之”字形打印矩阵

    给定一个矩阵matrix,按照“之”字形的方式打印这个矩阵,例如:

    [ 1  2  3  4 
      5  6  7  8 
      9 10 11 12 ]
    

    “之”字形打印的结果为:1 2 5 9 6 3 4 7 10 11 8 12

    要求:额外空间复杂度为O(1)。

    思路

    从左上角 matrix[0][0] 位置引出两点 A、B,点 A 运动规则是向右走,遇到边界向下走,点 B 运动规则是向下走,遇到边界向右走。每走一步,两点形成的路径就是“之”字的某一条路径。再设定一个 flag 变量,来表示从 A -> B 的路径和从 B -> A 的路径。

    实现

    public static void zigPrint(int[][] matrix) {
        int row = matrix.length - 1;
        int col = matrix[0].length - 1;
        // A
        int tR = 0;
        int tC = 0;
        // B
        int dR = 0;
        int dC = 0;
        boolean flag = false; //true A -> B false B -> A
        while (tR != row + 1) {
            print(matrix, tR, tC, dR, dC, flag);
            //点 A 判断条件是 tC,所以 tC 放在 tR 后,防止 tC 值的改变对 tR 值的判断造成影响
            tR = tC == col ? tR + 1 : tR;
            tC = tC == col ? tC : tC + 1;
            //点 B 判断条件是 dR,所以 dR 放在 dC 后,防止 dR 值的改变对 dC 值的判断造成影响
            dC = dR == row ? dC + 1 : dC;
            dR = dR == row ? dR : dR + 1;
            flag = !flag;
        }
    }
    
    private static void print(int[][] matrix, int tR, int tC, int dR, int dC, boolean flag) {
        if (flag) {
            // true A -> B
            while (tR != dR + 1) {
                System.out.printf("%3d", matrix[tR++][tC--]);
            }
        } else {
            // false B -> A
            while (dR != tR - 1) {
                System.out.printf("%3d", matrix[dR--][dC++]);
            }
        }
    }
    

    测试

    public static void main(String[] args) {
        int[][] matrix = {
            {1, 2, 3, 4},
            {5, 6, 7, 8},
            {9, 10, 11, 12},
        };
        zigPrint(matrix);
    }
    
    output:
    1  2  5  9  6  3  4  7 10 11  8 12
    

    小总结

    在解题过程中,能使用基础数据类型一定要使用,因为其传值的特性有助于限制变量的作用域。当其作用域可控,算法就更容易理解。

    在运算过程中,这个部分需要注意,前后顺序不同结果会受到影响,注意运算的逻辑:

    //点 A 判断条件是 tC,所以 tC 放在 tR 后,防止 tC 值的改变对 tR 值的判断造成影响
    tR = tC == col ? tR + 1 : tR;
    tC = tC == col ? tC : tC + 1;
    //点 B 判断条件是 dR,所以 dR 放在 dC 后,防止 dR 值的改变对 dC 值的判断造成影响
    dC = dR == row ? dC + 1 : dC;
    dR = dR == row ? dR : dR + 1;
    

    问题四:在行列都排好序的矩阵中找数

    给定一个有 N * M 的整型矩阵 matrix 和一个整数 K,matrix 的每一行和每一 列都是排好序的。实现一个函数,判断 K 是否在 matrix 中。

    例如:

    [ 0 1 2 5
      2 3 4 7
      4 5 7 9 ]
    

    如果 K 为7,返回 true;如果 K 为6,返回 false。
    要求:时间复杂度为 O(N + M),额外空间复杂度为 O(1)。

    思路

    选择右上角的数 a 比较
    	> a 排除 a 左侧的数,往下寻找
    	< a 排除 a 下方的数,往左寻找
    按照这个策略比较,最多只需要遍历 N + M 个数就可以知道是否找到数
    

    实现

    public static boolean findNumber(int[][] matrix, int number) {
        int dR = matrix.length - 1;
        int dC = 0;
        int tR = 0;
        int tC = matrix[0].length - 1;
        while (tR <= dR && tC >= dC) {
            if (matrix[tR][tC] == number) {
                return true;
            }
            if (matrix[tR][tC] < number) {
                tR++;
            } else {
                tC--;
            }
        }
        return false;
    }
    

    测试

    public static void main(String[] args) {
        int[][] matrix = {
            {1, 2, 3, 4, 5},
            {6, 7, 8, 9, 10},
            {11, 12, 13, 14, 15},
            {16, 17, 18, 19, 20},
            {21, 22, 23, 24, 25}
        };
    
        for (int i = 0; i < 10000; i++) {
            int number = (int) (Math.random() * 100);
            System.out.println("number = " + number);
            System.out.println(findNumber(matrix, number));
        }
    }
    
  • 相关阅读:
    flink-sql-client使用kafka表格
    flink 使用sql实现kafka生产者和消费者
    利用scan迁移部分单点redis数据到RedisCluster
    flink按事件时间排序
    Linux下面 多线程死锁问题的调试
    大数据开发工具漫谈
    如何撰写一个分布式计算平台的作业调度器?
    (随用随总结)Linux下面的特殊权限&不同的文件类型
    【javascript小案例】从0开始实现一个俄罗斯方块
    mysqldumpslow简单使用方法-mysqldumpslow详细用法
  • 原文地址:https://www.cnblogs.com/chenxianbin/p/11937431.html
Copyright © 2011-2022 走看看