题目描述
描述:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下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]
数据范围:
0 <= matrix.length <= 100
0 <= matrix[i].length <= 100
输入:[[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,2,3,1],[4,5,6,1],[4,5,6,1]]
返回值:[1,2,3,1,1,1,6,5,4,4,5,6]
解题思路
顺时针打印矩阵:最直观的想法是,设置bool类型的标记访问数组flag。使用变量m表示矩阵行数,变量n表示矩阵列数,变量loop表示当前循环圈数,起始为1,矩阵的循环圈数等于行数的一半,由于整数除法是向下取整,故如果矩阵行数为奇数时应该单独处理,变量x表示当前横坐标,起始为0,变量y表示当前纵坐标,起始为0,然后每一次循环过程中,按照“右下左上”的顺序进行处理。
vector<int> printMatrix(vector<vector<int> > matrix)
{
vector<int> result; //结果数组
int m = matrix.size(), n = matrix[0].size(); //m是长度 n是宽度
//cout<<m<<" "<<n<<endl;
vector<vector<bool>> flag(m,vector<bool>(n,false)); //标志数组
int x = 0,y = 0; //x是起始横坐标 y是起始纵坐标
int loop = 1; //loop是循环圈数
while(loop <= m/2) //循环圈数是行数的一半
{
//cout<<x<<" "<<y<<endl;
while(y < n && !flag[x][y]) //右
{
result.push_back(matrix[x][y]); //加入结果
flag[x][y]=true; //标记访问
y++; //向右加一
}
y--; //出界
x++; //下一行
//cout<<x<<" "<<y<<endl;
while(x < m && !flag[x][y]) //下
{
result.push_back(matrix[x][y]); //加入结果
flag[x][y]=true; //标记访问
x++; //向下加一
}
x--; //出界
y--; //下一列
//cout<<x<<" "<<y<<endl;
while(y >= 0 && !flag[x][y]) //左
{
result.push_back(matrix[x][y]); //加入结果
flag[x][y]=true; //标记访问
y--; //向右加一
}
y++; //出界
x--; //下一行
//cout<<x<<" "<<y<<endl;
while(x >=0 && !flag[x][y]) //上
{
result.push_back(matrix[x][y]); //加入结果
flag[x][y]=true; //标记访问
x--; //向下加一
}
x++; //出界
y++; //下一列
loop++; //循环圈数加一
}
if(m%2!=0) //处理奇数
{
while(y < n && !flag[x][y]) //右
{
result.push_back(matrix[x][y]); //加入结果
flag[x][y]=true; //标记访问
y++; //向右加一
}
y--;
}
return result;
}
优化:但是我们发现上面的代码由于x和y的出界,所以经常需要回溯,并且还要进行下一轮的起始处理,所以代码较为冗余,其实我们可以通过预判来同时解决出界回溯和下一轮起始处理这两个问题,其中预判又需要先将第一个起始元素单独处理加入数组,由于先加入故先要对空二维数组进行特殊判断,下述代码是对上述代码进行修改优化后的代码。
vector<int> printMatrix(vector<vector<int> > matrix)
{
vector<int> result; //结果数组
int m = matrix.size(), n = matrix[0].size(); //m是长度 n是宽度
vector<vector<bool>> flag(m,vector<bool>(n,false)); //标志数组
int x = 0,y = 0; //x是起始横坐标 y是起始纵坐标
int loop = 1; //loop是循环圈数
//先标记第一个元素再通过预判来解决从而避免回溯减少代码量
//由于先加了第一个元素 所以需要特殊判断数组
if(n==0) //矩阵为空 注意不是m==0 [[]] m=1 n=0
return result; //返回空数组
result.push_back(matrix[x][y]); //加入第一个元素
flag[x][y]=true; //标记访问
while(loop <= m/2) //循环圈数是行数的一半
{
while((y+1) < n && !flag[x][y+1]) //右
{
result.push_back(matrix[x][++y]); //加入结果
flag[x][y]=true; //标记访问
}
while((x+1) < m && !flag[x+1][y]) //下
{
result.push_back(matrix[++x][y]); //加入结果
flag[x][y]=true; //标记访问
}
while((y-1) >= 0 && !flag[x][y-1]) //左
{
result.push_back(matrix[x][--y]); //加入结果
flag[x][y]=true; //标记访问
}
while((x-1) >=0 && !flag[x-1][y]) //上
{
result.push_back(matrix[--x][y]); //加入结果
flag[x][y]=true; //标记访问
}
loop++; //循环圈数加一
}
if(m%2!=0) //处理奇数
{
while((y+1) < n && !flag[x][y+1]) //右
{
result.push_back(matrix[x][++y]); //加入结果
flag[x][y]=true; //标记访问
}
}
return result;
}
优化:但是我们又发现上面的两种方法都使用了额外的存储空间,那么可不可以写出空间复杂度为O(1)的程序呢?可以!设置上边界、下边界、左边界、右边界!然后依次按照上边界从左到右、上边界向下,右边界从上到下,右边界向左,下边界从右向左、下边界向上,左边界从下向上、左边界向右,直至左右边界重合。
vector<int> printMatrix(vector<vector<int> > matrix)
{
vector<int> result;
int m = matrix.size(),n = matrix[0].size();
//左边界 右边界 上边界 下边界
int left = 0, right = n-1, up = 0, down = m-1;
//直至边界重合
while(left<=right && up<=down)
{
//上边界从左到右
for(int i = left;i <= right;i++)
result.push_back(matrix[up][i]);
//上边界向下
up++;
//如果越界则退出
if(up>down)
break;
//右边界从上到下
for(int i = up;i <= down;i++)
result.push_back(matrix[i][right]);
//右边界向左
right--;
//如果越界则退出
if(right<left)
break;
//下边界从右向左
for(int i = right;i >= left;i--)
result.push_back(matrix[down][i]);
//下边界向上
down--;
//如果越界则退出
if(down<up)
break;
//左边界从下向上
for(int i = down;i >= up;i--)
result.push_back(matrix[i][left]);
//左边界向右
left++;
//如果越界则退出
if(left>right)
break;
}
return result;
}