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; } };