算法很美(四)

四、多维数组与矩阵

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

public class PrintTwoDArr {
    public static void main(String[] args) {
        int[][] matrix = {
                {1,2,3,4},
                {5,6,7,8},
                {9,10,11,12},
                {13,14,15,16},
        };
        print(matrix);
    }

    static void print(int[][] matrix) {
        //左上角行、左上角列、右下角行、右下角列
        int leftUpRow = 0, leftUpCol = 0, rightDownRow = matrix.length - 1, rightDownCol = matrix[0].length - 1;
        while (leftUpRow<=rightDownRow && leftUpCol<=rightDownCol) {
            int r = leftUpRow, c = leftUpCol;
            //上面一条边
            while (c <= rightDownCol) {
                System.out.print(matrix[r][c++] + "  ");
            }
            //恢复
            c = rightDownCol;
            r++;
            //右边的一条边
            while (r <= rightDownRow) {
                System.out.print(matrix[r++][c] + "  ");
            }
            //恢复
            r = rightDownRow;
            c--;
            //下面的一条边
            while (c >= leftUpCol) {
                System.out.print(matrix[r][c--] + "  ");
            }
            //恢复
            c = leftUpCol;
            r--;
            //左边的一条边
            while (r > leftUpRow) {
                System.out.print(matrix[r--][c] + "  ");
            }
            leftUpRow++;
            leftUpCol++;
            rightDownRow--;
            rightDownCol--;
        }
    }
}

2、零所在的行列清零

矩阵中含有某个元素0,将其所在的行和列清零
1 2 3 4
5 6 0 8
9 0 11 12
13 14 15 16
输出:
1 0 0 4
0 0 0 0
0 0 0 0
13 0 0 16

public class CleanZeroInTwoDArr {
    public static void main(String[] args) {
        int[][] matrix = {
                {1,2,3,0},
                {5,6,0,8},
                {9,0,11,12},
                {13,14,15,16},
        };
        solve(matrix);
        printMatrix(matrix);
    }

    public static void solve(int[][] matrix) {
        int M = matrix.length;
        int N = matrix[0].length;
        //记录哪些行出现0
        int[] rowRecord = new int[M];
        //记录哪些列出现0
        int[] colRecord = new int[N];
        for (int i = 0; i < M; i++) {
            for (int j = 0; j < N; j++) {
                if (matrix[0][0]==0){
                    //标记
                    rowRecord[i]=1;
                    colRecord[j]=1;
                }
            }
        }
        for (int row = 0; row < M; row++) {
            for (int col = 0; col < N; col++) {
                //当前的行或列,被标记了,这个元素就应该变为0
                if (rowRecord[row] ==1 || colRecord[col] ==1) {
                    matrix[row][col] = 0;
                }
            }
        }
    }

    public static void printMatrix(int[][] matrix) {
        for (int[] arr: matrix) {
            for (int e:arr) {
                System.out.print(e+"\t");
            }
            System.out.println();
        }
    }
}

一点小疑惑:这道题我检查了很多遍,甚至在码云上扣下了源代码来对比,但我的编译器输出仍然是原来一模一样的数组,也没有报错,所以还是学习下思路吧,求大家看后给我个评论呀。

3、Z字形打印二维数组

Z字形打印数组,相当于打印斜线

img

public class PrintMatrixZ {
    public static void main(String[] args) {
        int[][] matrix = {
                {1,2,3,4},
                {5,6,7,8},
                {9,10,11,12},
                {13,14,15,16},
        };
        print(matrix);
    }

    private static void print(int[][] matrix) {
        int r=0, m=matrix.length;
        int c=0, n=matrix[0].length;
        boolean l2r = true;  //左到由left to right
        while (r<m && c<n) {
            //左下到右上的斜线
            if (l2r) {
                System.out.print(matrix[r][c]+"  ");
                //在第一行,列未到边界,只能向右走
                if (r==0 && c<n-1) {
                    l2r = !l2r;  //切方向
                    c++;
                    continue;
                }
                //现在最后一列,只能向下走
                else if (r>0 && c==n-1) {
                    l2r = !l2r;
                    r++;
                    continue;
                }else {
                    //继续上坡
                    r--;
                    c++;
                }
            }
            //反之,下坡
            else {
                System.out.print(matrix[r][c]+"  ");
                //走到第一列,只能向下走
                if (c==0 &&r<m-1) {
                    l2r = !l2r;
                    r++;
                    continue;
                }
                //到最后一行,只能右走
                else if (r==m-1) {
                    l2r = !l2r;
                    c++;
                    continue;
                }else {
                    //下坡
                    r++;
                    c--;
                }
            }
        }
    }
}

4、子数组最大累加和

给定一个数组arr,返回子数组最大累加和

如:arr={1,-2,3,5,-2,6,-1} 所有的子数组中[3,5,-2,6]累加最大和12,则返回12

public class MaxSubArray {
    //暴力解法O(n²)
    static void findByForce(int[] arr) {
        int maxSum = arr[0];

        for (int j = 0; j < arr.length; j++) {
            int sum = arr[j]; //某个元素为子数组的第一个元素
            int maxOfJ = sum;
            for (int i = j+1; i < arr.length; i++) {
                sum += arr[i]; //累加后续元素
                if (sum >maxOfJ) {
                    maxOfJ = sum;
                }
            }
            if (maxOfJ>maxSum) {
                maxSum =maxOfJ;
            }
        }
        System.out.println(maxSum);
    }

    //递推法O(n)
    static int findByDp(int[] arr) {
        int sumJ = arr[0]; //前J个元素的最大贡献值
        int max= sumJ;
        int left=0, right=0;
        for (int j = 1; j < arr.length; j++) {
            if (sumJ>=0) { //左子表的最大和为正,继续向后累加
                sumJ += arr[j];
            }else {
                sumJ = arr[j];
                left = j;  //丢弃前部分和的同时,更新left
            }
            if (sumJ>max) {
                max = sumJ;
                right = j; //更新max的同时更新right
            }
        }
        return max;
    }

    public static void main(String[] args) {
        int[] arr = {1,-2,3,5,-2,6,-1};
        findByForce(arr);
        findByDp(arr);
    }
}

5、子矩阵最大累加和

给定一个矩阵matrix, 其中的值有正、有负、有零,返回子矩阵最大累加和

如:给出matrix

-1 -1 -1

-1 2 2

-1 -1 -1

其中最大累加和的子矩阵是2 2

所以返回4

import java.util.Arrays;

public class MaxSubMatrix {
    public static void main(String[] args) {
        int[][] matrix = {
                {-1, -1, -1},
                {-1, 2, 2},
                {-1, -1, -1}
        };
        int res = maxSum(matrix);
        System.out.println(res);
    }

    private static int maxSum(int[][] matrix) {  //O(N^3)
        int beginRow = 0; //以它为起始行

        final int M = matrix.length;
        final int N = matrix[0].length;

        int[] sums = new int[N]; //按列求和
        int max = 0; //历史最大子矩阵和
        while (beginRow < M) { //起始行
            for (int i = beginRow; i < M; i++) { //从起始行到第i行
                //按列累加
                for (int j = 0; j < N; j++) {
                    sums[j] += matrix[i][j];
                }
                //累加完成
                //求出sums的最大和子数组
                int t = MaxSubArray.findByDp(sums);
                if (t > max) {
                    max = t;
                }
            }
            //另起一行作为起始行,把sums清零
            Arrays.fill(sums,0); //将sums的每个元素设为0
            beginRow++;
        }
        return max;
    }
}
posted @ 2020-03-17 14:41  我在吃大西瓜呢  阅读(178)  评论(0编辑  收藏  举报