目录
一 螺旋矩阵I
题目描述:
解题思路:
这道题呢跟我们日常生活中剥洋葱一样,我们首先把皮给剥了然后再剥里面的。这道题也是类似的我们定义一个开始点和一个结束点将其边界全部遍历。然后都往里面走。具体请看代码:
class Solution { public: vector<int> spiralOrder(vector<vector<int>>& matrix) { vector<int>ans; //记录答案 int row1=0;//边界开始位置的行 int col1=0;//边界结束开始位置的列 int row2=matrix.size()-1;//边界结束的列 int col2=matrix[0].size()-1;//边界结束的列 while(row1<=row2&&col1<=col2)//如果两个点错过了说明已经遍历结束了 { Print(matrix,ans,row1++,col1++,row2--,col2--); } return ans; } void Print(vector<vector<int>>&matrix,vector<int>&ans,int row1,int col1,int row2,int col2) { if(row1==row2)//两个点的行变成一样了直接从这一行遍将其放入答案中 { for(int i=col1;i<=col2;i++) { ans.push_back(matrix[row1][i]); } return; } //列相等 if(col1==col2) {//直接将这一列遍历放入答案中 for(int i=row1;i<=row2;i++) { ans.push_back(matrix[i][col1]); } return; } //说明行和列不相等将边框打印 //提前记录一下方便后序遍历 int rowStart=row1; int colStart=col1; while(colStart<col2)//打印最上层的边框 { ans.push_back(matrix[row1][colStart++]); } //打印往下走的边框 while(rowStart<row2) { ans.push_back(matrix[rowStart++][col2]); } //打印往左走的 while(colStart>col1) { ans.push_back(matrix[row2][colStart--]); } //往上走 while(rowStart>row1)//注意这里是大于防止重复遍历 { ans.push_back(matrix[rowStart][col1]); rowStart--; } } };
二.螺旋矩阵II
题目描述:
解题思路:会了上一题这题就直接秒杀了只需要提前开好空间将值放入数组即可,和上题基本不变。具体请看代码
class Solution { public: int num=1; vector<vector<int>> generateMatrix(int n) { vector<vector<int>>ans(n,vector<int>(n)); int row1=0; int col1=0; int row2=n-1; int col2=n-1; while(row1<=row2&&col1<=col2) { Print(ans,row1++,col1++,row2--,col2--); } return ans; } void Print(vector<vector<int>>&ans,int row1,int col1,int row2,int col2) { if(row1==row2)//两个点的行变成一样了直接从这一行遍将其放入答案中 { for(int i=col1;i<=col2;i++) { ans[row1][i]=num++; } return; } //列相等 if(col1==col2) {//直接将这一列遍历放入答案中 for(int i=row1;i<=row2;i++) { ans[i][col1]=num++; } return; } //说明行和列不相等将边框打印 //提前记录一下方便后序遍历 int rowStart=row1; int colStart=col1; while(colStart<col2)//打印最上层的边框 { ans[row1][colStart++]=num++; } //打印往下走的边框 while(rowStart<row2) { ans[rowStart++][col2]=num++; } //打印往左走的 while(colStart>col1) { ans[row2][colStart--]=num++; } //往上走 while(rowStart>row1)//注意这里是大于防止重复遍历 { ans[rowStart][col1]=num++; rowStart--; } } };
三.旋转图像
题目描述:
解题思路:
本题的实现思路和上题差不多首先将外面的旋转弄好,在往里面旋转直到行和行相等了。
如上图所示,由于第 11 步 D \rightarrow AD→A 已经将 AA 覆盖(导致 AA 丢失),此丢失导致最后第 44 步 A \rightarrow BA→B 无法赋值。为解决此问题,考虑借助一个「辅助变量 tmptmp 」预先存储 AA ,此时的旋转操作变为:
暂存 \ tmp = A \\ A \leftarrow D \leftarrow C \leftarrow B \leftarrow tmp
暂存 tmp=A
A←D←C←B←tmp
对应代码:
class Solution { public: void rotate(vector<vector<int>>& matrix) { int row1=0; int col1=0; int row2=matrix.size()-1; int col2=matrix[0].size()-1; while(row1<row2)//如果他们相等了说明不用旋转了 { Rotate(matrix,row1++,col1++,row2--,col2--); } } void Rotate(vector<vector<int>>&matrix,int row1,int col1,int row2,int col2) { for(int i=0;i<col2-col1;i++) { int tmp=matrix[row1][col1+i];//旋转的第一个元素 matrix[row1][col1+i] =matrix[row2-i][col1];//这一组最后一个元素覆盖第一个位置 matrix[row2-i][col1]=matrix[row2][col2-i];//依次覆盖 matrix[row2][col2-i]=matrix[row1+i][col2]; matrix[row1+i][col2]=tmp;//将第一个元素放到正确的位置 } } };
四对角线遍历
题目描述:
解题思路:
正对角线有一个性质:同一条对角线上每个点的(x,y)坐标之和相等。通过观察易知矩阵一共有 m + n - 1 条对角线,每条对角线要么从左下到右上,要么从右上到左下,先求出这两个端点,再根据当前循环的方向进行遍历。
根据对角线性质,只要知道端点的横纵坐标之一,就可以得到另一维坐标,图例中我们只关心横坐标。
对应代码:
class Solution { public: vector<int> findDiagonalOrder(vector<vector<int>>& mat) { int endRow=mat.size()-1; int endCol=mat[0].size()-1; int row1=0; int col1=0; int row2=0; int col2=0; vector<int>ans; bool fromUp=false; while(row1<=endRow) { Print(mat,ans,row1,col1,row2,col2,fromUp); row1=col1==endCol?row1+1:row1;// col1=col1==endCol?col1:col1+1; col2=row2==endRow?col2+1:col2; //注意这里要先判断否则会错过最后一些数据老铁可以思考一下 row2=row2==endRow?row2:row2+1; fromUp=!fromUp;//方向每次取反 } return ans; } void Print(vector<vector<int>>&mat,vector<int>&ans,int row1,int col1,int row2,int col2,bool fromUp) { if(fromUp)//如果为true说明是从上面往下面遍历 { while(row1<=row2) { ans.push_back(mat[row1++][col1--]); } } else//说明是从下面往上面遍历 { while(row2>=row1) { ans.push_back(mat[row2--][col2++]); } } } };
五.矩阵置零
题目描述:
解题思路:
1.使用第0列和第0行来标记某一行和某一列是否要被置0,遍历结束之后在遍历一次更加第0行和第0列来判断某个位置是否要置0,但是问题来了如果第0行和第0列里面就有0了,所以我们要用一个变量记录一下单独拿出来处理即可,具体请看代码。
class Solution { public: void setZeroes(vector<vector<int>>& matrix) { bool iscol=false;//标记第一行是否要置0 bool isrow=false;//标记第一列是否要置0; for(int i=0;i<matrix[0].size();i++) { //遍历第一行是否要置0 if(matrix[0][i]==0) isrow=true; } for(int i=0;i<matrix.size();i++) { if(matrix[i][0]==0) { iscol=true; } } //用0行和第0列的元素标记这一行和这一列是否需要置0 for(int i=1;i<matrix.size();i++) { for(int j=1;j<matrix[0].size();j++) { if(matrix[i][j]==0) { matrix[0][j]=0; matrix[i][0]=0; } } } //在遍历一次完成置0 for(int i=1;i<matrix.size();i++) { for(int j=1;j<matrix[0].size();j++) { if(matrix[0][j]==0||matrix[i][0]==0) //对应第0行的位置和第一列的位置 //只要有一个为0就说明这个位置需要被置为0; { matrix[i][j]=0; } } } //单独判断第一行和第一列是否需要置0 if(isrow)//说明第0行需要置0 { for(int i=0;i<matrix[0].size();i++) { matrix[0][i]=0; } } if(iscol)//说明第一列需要置0 { for(int i=0;i<matrix.size();i++) { matrix[i][0]=0; } } //处理结束 } };
六 .有序矩阵中第k小的数
题目描述:
解题思路:
思路非常简单:
1.找出二维矩阵中最小的数 left,最大的数 right,那么第 k 小的数必定在 left ~ right 之间mid=(left+right)/2;在二维矩阵中寻找小于等于 mid 的元素个数 count.
2.若这个 count 小于 k,表明第 k 小的数在右半部分且不包含 mid,即 leftd=mid+1, right=right,又保证了第 k小的数在 left ~ right之间
3.若这个 count大于 k,表明第 k 小的数在左半部分且可能包含 mid,即left=left, right=mid,又保证了第 k 小的数在 left ~right 之间
4.因为每次循环中都保证了第 k小的数在 left~ right 之间,当 left==right 时,第 k 小的数即被找出,等于 right
对应代码:
class Solution { public: struct Info { Info(int n,int cnt) { near=n; num=cnt; } int near; int num; }; int kthSmallest(vector<vector<int>>& matrix, int k) { int n=matrix.size(); int left=matrix[0][0]; int right=matrix[n-1][n-1]; int ans=0; while(left<=right) { int mid=(left+right)/2;//取中点二分 Info* ret=getLessEqualNum(matrix,mid);//看一下小于等于mid的有多少个 if(ret->num<k)//如果小于k说明二分的中点小了 { left=mid+1; } else{//大于等于更新ans; right=mid-1; ans=ret->near; } } return ans;//返回ans } Info*getLessEqualNum(vector<vector<int>>&matrix,int val) { int m=0; int n=matrix[0].size()-1; int num=0; int near=INT_MIN;//记录小于等于val离val最近的数 while(m<matrix.size()&&n>=0) { if(matrix[m][n]<=val) { num+=n+1;//把小于val的个数加起来 near=max(near,matrix[m][n]);//更新near m++; } else { n--; } } return new Info(near,num);//返回结果 } };