顺时针打印矩阵

题目:输入一个矩阵, 按照从外向里以顺时针的顺序依次打印出每一个数字.
例如, 如果输入以下矩阵:
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.

分析:
顺时针打印矩阵是从矩阵的左上角(0,0)开始,按照从左到右,从上到下,从外到内的顺序打印矩阵每个位置的元素.
如果我们将每一次打印作为一个循环,每一次是按照里外划分的.如果这么划分的话,显然1x,2x的矩阵(x任意大)只有一次循环.
要打印两次以上的矩阵至少要有三行.同时,x1,x2的矩阵也只有一次循环.所以我们总结只有当行列同时大于2的时候才能至少
有两次打印.接下来判断循环终止的条件.
我们注意到每次最后一圈的打印一定要么是1行x列,要么是x行1列,要么是2行x列,要么是x行2列.而且每一圈的打印起始位置一定是
(k,k),假设矩阵是m*n的.我们可以继续测试:3行x列或是x行3列,4行x列或是x行4列都是有两次打印的,这里x>2.由此可见行列的
最小值决定了打印的次数,并且1,2对应1次;3,4对应2次;5,6对应3次…
那么何时终止打印? 现在假设m,n是矩阵行列的最大值(从0开始),当min{m,n}={0,1}时,最后一圈的起始位置是(0,0);
当min{m,n}={1,2}时,最后一圈起始位置是(1,1);当min{m,n}={3,4}时,最后一圈起始位置是(2,2)…
由此可见当2k>=min{m,n}时,这一定是最后一圈打印,即循环到此终止.
我们可以设计一个while循环,循环终止条件是2k>=min{m,n}.然后在每一层循环中考虑打印的内容.
最外层的打印顺序是(0,0)->(0,n)->(m,n)->(m,0)->(1,0),继而是(1,1)->(1,n-1)->(m-1,n-1)->(m-1,1)->(2,1),…
对比两层有一个特点.设某一层5个位置分别是(k_1,k_2),(k_1,l_2),(p_1,l_2),(p_1,q_2),(r_1,q_2).
则有: l_2+k_2=n, k_1+p_1=m, l_2+q_2=n, p_1+r_1=m-1(或r_1=k_1+1)
简单来说,就是第二个拐点和第一个拐点的列和等于n,行数相等;第三个拐点和第二个拐点的行和等于m,列数相等;第四个拐点
和第三个拐点的列和等于n,行数相等;最后一个拐点行数等于第一个拐点行数+1,列数相等.

i(n),j(n)表示第n圈的每个拐点的位置.但是鉴于矩阵不总是方阵,所以一圈不总是个圈.它可能只是一步,也有两步,
三步的情况,最多四步.一步的情况对应只有一行;两步的情况对应只有一列,至少两行以上;三步的情况对应只有两行,至少两列以上;
其余的情况都是四步.
上面的是四步打印的情况,接下来分别介绍一步,两步,三步的情况:
一步:(k,k)->(k,n-k)
两步:(k,k)->(k,k)->(m-k,k)
三步:(k,k)->(k,n-k)->(k+1,n-k)->(k+1,k)

于是,我们现在也知道了循环的每一步的打印方式,它跟每一次的起始位置和m,n有关.

代码:

package 剑指offer29_顺时针打印矩阵;

public class PrintMatrixInCircle {

    public static void PrintCycles(int m, int n, int[][] array){
        if(m < 0 || n < 0){
            return;
        }
        int k = 0;
        //当s=min{m,n}为奇数时,要求2k+1<=s;否则要求2k<=s.
        while(2*k+1 <= Math.min(m,n) || 2*k <= Math.min(m, n)){
            PrintPerCycle(m, n, k, array);
            k++;
        }
    }

    public static void PrintPerCycle(int m, int n, int k, int[][] array) {
        //如果最后一圈只有一行,那么只打印这一行数据
        if(2*k == m){
            for(int col = k; col < n-k+1; col++){
                System.out.println(array[k][col]);
            }
            //如果最后一圈至少两行,并且只有一列,那么只打印这一列数据
        }else if(2*k+1 <= m && 2*k == n){
            for(int row = k; row < m-k+1; row++){
                System.out.println(array[row][k]);
            }
            //如果最后一圈只有两行,并且至少有两列以上,那么先打印一行,再打印下一行
        }else if(2*k+1 == m && 2*k+1 <= n){
            for(int col = k; col < n-k+1; col++){
                System.out.println(array[k][col]);
            }
            for(int col = n-k; col > k-1; col--){
                System.out.println(array[k+1][col]);
            }
            //如果是其他情况,照此打印
        }else{
            for(int col = k; col < n-k+1; col++){
                System.out.println(array[k][col]);
            }
            for(int row = k+1; row < m-k+1; row++){
                System.out.println(array[row][n-k]);
            }
            for(int col = n-k-1; col > k-1; col--){
                System.out.println(array[m-k][col]);
            }
            for(int row = m-k-1; row > k; row--){
                System.out.println(array[row][k]);
            }
        }
    }

    public static void main(String[] args) {
        int m,n;
        int [][] array2 = {{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}};
        m = array2.length-1;
        n = array2[0].length-1;
        PrintCycles(m, n, array2); //成功

        System.out.println("----------------------------------------------");
        int [][] array3 = {{1, 2, 3, 4}, {5, 6, 7, 8}};
        m = array3.length-1;
        n = array3[0].length-1;
        PrintCycles(m, n, array3); //成功

        System.out.println("-----------------------------------------------");
        int [][] array4 = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
        m = array4.length-1;
        n = array4[0].length-1;
        PrintCycles(m, n, array4);//成功

        System.out.println("-----------------------------------------------");
        int [][] array5 = {{1, 2, 3}, {5, 6, 7}, {9, 10, 11}, {13, 14, 15}};
        m = array5.length-1;
        n = array5[0].length-1;
        PrintCycles(m, n, array5);//成功
    }
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值