29 顺时针打印矩阵(四-画图让抽象问题形象化)

题目描述:

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

例如,如果输入如下4 X 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.

 

测试用例:

 矩阵中有多行多列、矩阵中只有一行或者一列、数组中只有一列、数组中只有一行一列。(并没有考虑矩阵为空的情况)

 

解题思路:

1)^-^- 规律:每次圈的起始点都是(0,0)(1,1)(start,start)行列的数字相等

      ^-^- 一个矩阵能划分为多少个圈:找规律

      对于一个5*5的矩阵,最后一圈只有一个数字,对应的坐标为(2,2)。5>2*2

      对于一个6*6的矩阵,最后一圈只有一个数字,对应的坐标为(2,2)。6>2*2

     于是可以得出。让循环继续的条件是 columns>startX*2 并且 rows>startY*2

     ^-^- 可以把打印一圈分为四步:第一步,从左到右打印一行;第二步,从上到下打印一列;第三步,从右到左打印一行;第四步,从下到上打印一列。每一步根据起始坐标和终止坐标用循环打印出一行或者一列。

      

class Solution {
public:
    vector<int> printMatrix(vector<vector<int> > matrix) {
        if(matrix.size()==0)  //
            return result;
        
        int start = 0; //标志着第几圈
        int rows = matrix.size();
        int cols = matrix[0].size();
        while(rows>start*2 && cols>start*2){
            printMatrixInCircle( matrix,rows,cols,start);
            start++;
        }
        return result;
    }
    
    void printMatrixInCircle(vector<vector<int> > matrix,int rows,int cols,int start){
        int endCol = cols-1-start;
        int endRow = rows-1-start;
        
        //第一步:打印第一行(一定会打印)
        for(int col=start; col<=endCol; col++){
            result.push_back(matrix[start][col]);
        }
        //第二步:若行数大于等于二(即多于一行时),则会打印。即endRow-start>0
        if(endRow-start>0){
            for(int row=start+1; row<=endRow; row++)
                result.push_back(matrix[row][endCol]);
        }
        //第三步:至少两行两列
        if(endRow-start>0 && endCol-start>0){ //
            for(int col=endCol-1; col>=start; col--)
                result.push_back(matrix[endRow][col]);
        }
        //第三步:至少三行两列
        if(endRow-start>1 && endCol-start>0){
            for(int row=endRow-1; row>start; row--)
                result.push_back(matrix[row][start]);
        }
    }
private:
    vector<int> result;
};

2)int circle=((row<collor?row:collor)-1)/2+1;//圈数

3)定义四个关键变量,表示左上和右下的打印范围,限定一个圈。每次循环后缩小一圈

/*
    思想,用左上和右下的坐标定位出一次要旋转打印的数据,一次旋转打印结束后,往对角分别前进和后退一个单位。
    提交代码时,主要的问题出在没有控制好后两个for循环,需要加入条件判断,防止出现单行或者单列的情况。
 */
class Solution {
public:
    vector<int> printMatrix(vector<vector<int> > matrix) {
        int row = matrix.size();
        int col = matrix[0].size();
        vector<int> res;
         
        // 输入的二维数组非法,返回空的数组
        if (row == 0 || col == 0)  return res;
         
        // 定义四个关键变量,表示左上和右下的打印范围
        int left = 0, top = 0, right = col - 1, bottom = row - 1;
        
        while (left <= right && top <= bottom) //至少有一个元素时
        {
            // left to right 一定会打印
            for (int i = left; i <= right; ++i)  res.push_back(matrix[top][i]);
            // top to bottom 行数多于1行才会打印  here 只有一行时不会进入for循环,因此该处无需判断
            for (int i = top + 1; i <= bottom; ++i)  res.push_back(matrix[i][right]);
            // right to left //至少两行两列时,才会打印 top != bottom限制至少两行 for循环限制right-1>=left,即至少两行
            if (top != bottom)
                for (int i = right - 1; i >= left; --i)  res.push_back(matrix[bottom][i]);
            // bottom to top //至少三行两列  left != right限制两列 bottom - 1> top 至少三列
            if (left != right)
                for (int i = bottom - 1; i > top; --i)  res.push_back(matrix[i][left]);
            
            left++,top++,right--,bottom--; //缩小一圈
        }
        return res;
    }
};

4)顺着走,即向右->向下->向左->向上,一共要走(长*宽)步。遇到边界就改变方向,当向上碰到顶的时候,四个边界都缩小。思路简单,一个循环即可!

class Solution {
public:
    vector<int> printMatrix(vector<vector<int> > matrix) {
        vector<int> ret;
        if(matrix.empty())return ret;
             
        int x,y,cnt=matrix[0].size()*matrix.size(); //一共需要打印的个数
        //右rEdge 下dEdge 左lEdge 上uEdge
        int rEdge=matrix[0].size()-1,dEdge=matrix.size()-1,lEdge=0,uEdge=0;
        //bool first=true;
        for(x=0,y=0;cnt>0;cnt--){ //从(0,0)开始
            ret.push_back(matrix[x][y]);
            //go  right
            if(x==uEdge){  //上边界
                if(y<rEdge) //小于右边界时,向右移动。y++
                    y++;
                else if(y==rEdge) //到达右边界,向下移动
                    x++;
                continue; //不会再判断后面的if了
            }
             
            //down
            if(y==rEdge){
                if(x<dEdge)
                    x++;
                else if(x==dEdge){
                    y--;
                }
                continue; //不会再判断后面的if了
            }
             
            //left
            if(x==dEdge){
                if(y>lEdge)
                    y--;
                else if(y==lEdge){
                    x--;
                }
                continue; //不会再判断后面的if了
            }
             
            //up
            if(y==lEdge){
                if(x>uEdge+1)
                    x--;
                else if( x==uEdge+1){ //到达一圈的最后一个点
                    y++;
                    lEdge++;
                    uEdge++;
                    rEdge--;
                    dEdge--;
                }
                continue;
            }
            //;3
        }
        return ret;
    }
};

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

  

 

posted @ 2019-03-04 17:04  GuoXinxin  阅读(212)  评论(0编辑  收藏  举报