《剑指offer》面试题20 顺时针打印矩阵

题目描述:

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵:

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.


很容易想到的是 我们采用递归来处理这个问题。 但问题是我们如何判断打印的方向?不妨设置一个变量direct 表示我们打印的方向,比如direct=1 继续打印右边的矩阵元素,direct=2打印下面的元素。那如何判断走到边界处需要改变方向呢?很容易,我们对于访问过得元素置为0就可以了,这样就可以避免重复访问了。

#include <iostream>
#include "string.h"
using namespace std;

int a[1010][1010];
int m,n;
static int cnt;

void print(int i,int j,int c[][1010],int dir)
{
    cout<<c[i][j]<<' ';
    c[i][j]=0;              //once we print c[i][j],let it equals zero,so that it can not be printed again
    
    cnt++;
    if(cnt==n*m)
        return ;            //it showed we have printde the whole array,so it is the end of recurism
    
    if(dir==1)
    {
        if (c[i][j+1])        //go on to print a[i][j+1]
        {
            print(i, j+1, c, dir);
        }
        else
        {
            print(i+1,j,c,dir+1);    //end of line,so that have to change direct
        }
    }
    else if(dir==2)
    {
        if(c[i+1][j])
        {
            print(i+1, j, c, dir);
        }
        else
        {
            print(i,j-1,c,dir+1);
        }
    }
    else if(dir==3)
    {
        if(c[i][j-1])
        {
            print(i, j-1, c, dir);
        }
        else
        {
            print(i-1, j, c, dir+1);
        }
    }
    else if(dir==4)
    {
        if(c[i-1][j])
        {
            print(i-1, j, c, dir);
        }
        else
        {
            print(i, j+1, c,1);
        }
    }
    return ;
}
int main(int argc, const char * argv[])
{
    while(cin>>m>>n)
    {
        memset(a, 0, sizeof(a));
        cnt=0;
        for(int i=1;i<=m;i++)
            for(int j=1;j<=n;j++)
                cin>>a[i][j];
        int direct=1;
        if(n==1)
            direct=2;
        print(1, 1, a, direct);
        cout<<endl;
    }
    return 0;
}

输入为

4 4
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 

但是这样的递归程序有个问题,就是当二维数组的规模不是很大的时候是完全可以运行的,但规模增长的时候很有可能会栈溢出,而且时间效率也不高,我们需要另寻一种高效的方法来解决,最好是递推来解决。


我们可以想到每次打印的时候是一圈一圈的进行打印的,如下图所示:


图片来源于  http://zhedahht.blog.163.com/blog/static/254111742010111112236313/ 

我们假设 每一圈打印的起始点(左上角)坐标分别为startX,startY,右下角坐标为endX,endY,这两个点定义了我们打印的范围。我们需要做的就是依次打印出每个圈。

观察即可发现,起始点坐标依次是(0,0),(1,1)。。。判断边界条件是 startX *2<row && startY *2<column,其实startX=startY。

剩下的问题就是如何打印出每一圈。

首先 依次向右打印元素是必须的。

那什么样的情况下需要向下打印呢?由图可以看出 应该是 endX>startX 。

需要向左打印的条件是什么呢?也很明显,应该是endY>startX。 

那就剩下需要考虑向上打印完成完整的一圈打印了。思考一下便可得到  endX>startX+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.

输入:

输入可能包含多个测试样例,对于每个测试案例,

输入的第一行包括两个整数m和n(1<=m,n<=1000):表示矩阵的维数为m行n列。

接下来的m行,每行包括n个整数,表示矩阵的元素,其中每个元素a的取值范围为(1<=a<=10000)。

输出:

对应每个测试案例,输出一行,

按照从外向里以顺时针的顺序依次打印出每一个数字,每个数字后面都有一个空格。

样例输入:
4 41 2 3 45 6 7 89 10 11 1213 14 15 16
样例输出:
1 2 3 4 8 12 16 15 14 13 9 5 6 7 11 10 

程序如下:

#include <iostream>
#include "stdio.h"
using namespace std;

int a[1010][1010];
int m,n;

void print(int c[][1010],int row,int column,int start)
{
    int end_x=row-1-start;
    int end_y=column-1-start;
    
    for(int i=start;i<=end_y;i++)
        cout<<c[start][i]<<' ';
    
    if(end_x>start)
    {
        for(int j=start+1;j<=end_x;j++)
            cout<<c[j][end_y]<<' ';
    }
    
    if(start<end_x &&start<end_y)
    {
        for(int i=end_y-1;i>=start;i--)
            cout<<c[end_x][i]<<' ';
    }
    if(start<end_y&&start<end_x-1)
    {
        for(int j=end_x-1;j>start;j--)
            cout<<c[j][start]<<' ';
    }
}
int main()
{
    while (cin>>m>>n)
    {
        for(int i=0;i<m;i++)
            for(int j=0;j<n;j++)
                cin>>a[i][j];
       
        for(int i=0;2*i<m && 2*i<n;i++)
            print(a,m,n,i);
        cout<<endl;
    }
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值