顺时针打印矩阵(剑指OFFER 面试题29)

问题描述

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵:
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.
由于是以外圈到内圈的顺序依次打印,所以我们可以把矩阵想象成若干个圈,这样就能用一个循环来打印矩阵了,每次打印矩阵中的一个圈。

分析循环结束的条件:
假设这个矩阵的行数是rows,列数是columns,打印第一圈的左上角的坐标(0,0),第二圈的坐标是(1,1),以此类推,可以发现,左上角的坐标中行和列的值总是相同的,于是可以在矩阵中选取左上角为(start,start)的一圈作为我们分析的目标。
对于5✖5的矩阵来说,最后一圈只有一个数字,对应的坐标为2✖2,可以发现5>2✖2 。对于一个6✖6的矩阵来说,最后一圈有4个数字,其左上角的左边仍然为(2,2)。我们发现6>2✖2依然成立。于是可以得出,让循环继续的条件是:columns>startX ✖ 2且rows>startY ✖ 2 。

接下来分析如何打印一圈的功能:
第一步,从左到右打印一行;
第二步,从上到下打印一列;
第三步,从右到左打印一行;
第四部,从下到上打印一列。
每一步我们根据起始坐标用一个循环就能打印出一行或者一列了。

但是,

最后一圈可能只有一行、一列,甚只有一个数字,因此打印这样的圈就不用四步了。
我们需要仔细判断打印的前提条件。
第一步总是需要的,因为打印一圈至少有一步。如果只有一行,那就不用第二步了。也就是第二步需要的前提条件是终止行号大于起始行号。第三步打印的前提条件是圈内至少有两行两列,也就是说,除了要求终止行号大于起始行号,还要求终止列号大于起始列号。第四步,前提条件是至少有三行两列,因此要求终止行号比起始行号至少大2,同时终止列号大于起始列号。

如果思考时难以理解,可以通过画几幅图来帮助理解,这样往往能够快速的打开思路。

以下为该问题的参考代码:

using System;
using System.Collections.Generic;

namespace 顺时针打印矩阵
{
    class Program
    {
        static void Main(string[] args)
        {
            //Test1:二维数组有多行多列
            Console.WriteLine("Test1:");
            int[][] matrix = new int[5][];
            matrix[0] = new int[] { 1, 2, 3, 4, 5 };
            matrix[1] = new int[] { 6, 7, 8, 9, 10 };
            matrix[2] = new int[] { 11, 12, 13, 14, 15 };
            matrix[3] = new int[] { 16, 17, 18, 19, 20 };
            matrix[4] = new int[] { 21, 22, 23, 24, 25 };

            List<int> list = new List<int>();
            list = PrintMatrix(matrix);
            PrintList(list);

            //Test2:二维数组就一列
            Console.WriteLine("\nTest2:");
            int[][] matrix1 = new int[5][];
            matrix1[0] = new int[] { 1 };
            matrix1[1] = new int[] { 2 };
            matrix1[2] = new int[] { 3 };
            matrix1[3] = new int[] { 4 };
            matrix1[4] = new int[] { 5 };

            List<int> list1 = new List<int>();
            list1 = PrintMatrix(matrix1);
            PrintList(list1);

            //Test3:二维数组为空
            Console.WriteLine("\nTest3:");
            int[][] matrix2 = null;

            List<int> list2 = new List<int>();
            list2 = PrintMatrix(matrix2);
            PrintList(list2);
        }

        public static List<int> PrintMatrix(int[][] matrix)
        {
            //如果传入的数组为空,返回空
            if (matrix == null)
                return null;
            //如果传入的数组不为空,说明有行值,计算出行值
            int columns = matrix.Length;
            //如果行值大于1,则继续
            if (columns <= 0)
                return null;
            //如果行值大于1,说明有不止一行,判断列值是否大于1,大于则继续
            int rows = matrix[0].Length;
            if (rows <= 0)
                return null;

            List<int> list = new List<int>();
            int start = 0;

            //判断是否已经遍历完最后一圈
            while (columns > start * 2 && rows > start * 2)
            {
                //把每圈数字依次添加到List中
                list.AddRange(PrintMatrixInCircle(matrix, columns, rows, start));

                //当遍历完当前一圈,让起点的坐标更进一层
                start++;
            }

            return list;
        }

        /// <summary>
        /// 遍历一圈的值
        /// </summary>
        /// <param name="matrix">矩阵</param>
        /// <param name="columns">矩阵的行</param>
        /// <param name="rows">矩阵的列</param>
        /// <param name="start">圈的起始位置</param>
        /// <returns>一圈所有值的集合</returns>
        private static List<int> PrintMatrixInCircle(int[][] matrix, int columns, int rows, int start)
        {
            int endX = rows - 1 - start;
            int endY = columns - 1 - start;
            List<int> list = new List<int>();

            //从左往右打印一行
            for (int i = start; i <= endX; i++)
            {
                list.Add(matrix[start][i]);
            }

            //从上往下打印一列
            if (start < endY)
                for (int i = start + 1; i <= endY; i++)
                    list.Add(matrix[i][endX]);

            //从右往左打印一行
            if (start < endX && start < endY)
                for (int i = endX - 1; i >= start; i--)
                    list.Add(matrix[endY][i]);

            //从下往上打印一列
            if (start < endX && start < endY - 1)
                for (int i = endY - 1; i >= start + 1; i--)
                    list.Add(matrix[i][start]);

            return list;
        }

        /// <summary>
        /// 打印集合list中的所有元素
        /// </summary>
        /// <param name="list">集合</param>
        private static void PrintList(List<int> list)
        {
            if(list==null)
            {
                Console.WriteLine("null");
                return;
            }
            foreach(int item in list)
            {
                Console.Write(item + "\t");
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_33575542/article/details/80766389