题目
①循环不变量介绍
for、while等,循环体里面的代码是固定的,所以每次循环的逻辑都得尽量统一,这就是循环不变量原则。(下面具体介绍这种思想带来的优点)
1.视频引入(配合解析细节一起看)
螺旋矩阵
2.解析动画里面的细节
以n=5为例,第一圈的遍历只遍历四个元素。
如果第一圈每次都是遍历五个元素,那么就无法保证循环不变量。
理由:
循环里面的代码逻辑是一样的,如果第一行遍历五个元素,后面的代码就没办法连续遍历五个元素了(自己模拟就知道了!顶多只能再插入四个元素)
补充:不坚持循环不变量也能解决问题,例如行的话遍历5个元素,列的话遍历3个元素,也能把矩阵填满一圈。
但是这种分类讨论去处理会导致代码很冗余,而且逻辑也会很复杂。
所以坚持"循环不变量"这个原则是很有用的!
到这里,大家应该可以体会到循环不变量是怎样的一种思想!
②本题细节补充(思路在视频已经展现了)
n其实得分奇偶数。
n为奇数的话使用循环不变量会剩下中间元素没有填充。
n为偶数的话,就刚好填充完矩阵(可以自己以4*4矩阵试验,不过建议等整篇文章系统看完再实验)
得记录绕圈的次数,且得更新落脚点
circle是圈数
刚开始填充的是n-1个元素。经过一圈以后。填充的次数变成了 (n-1-2*circle)次
填充次数<=0停止填充
(也是因为停止填充的时候,n为奇数的矩阵中心还缺一个待填充。所以才分奇偶性)
落脚点更新:
x,y表示当前该填充的坐标
第一圈更新完之后,坐标是(1,0)。
第二圈开始执行时,填充的坐标为(1,1)。此时circle == 1
推导到一般情况就是每一圈结束之后,落脚点更改为:(circle,circle)
一种技巧:数组表示方向(这种技巧在图论的BFS邻接矩阵四个方向也有用到)
可以不使用这种代码,直接在for循环里面依次写四种代码表示(右下左上顺序的填充,不过有点麻烦)
③代码实现
class Solution
{
int dir[4][2]{
{0,1},//右
{1,0},//下
{0,-1},//左
{-1,0}//上
};
public:
vector<vector<int>> generateMatrix(int n)
{
vector<vector<int>>result(n,vector<int>(n));
if(n%2==1){ result[n/2][n/2]=n*n;}
int circle = 0;//每加一圈,填充数量减少2
int full = n-1; //每次循环填充的数量
int num=1;//该填充的数字大小
while(full>0){//full<=0就无法填充了
int x=circle,y=circle;//该填充位置(x,y) 。每次执行一圈都得更新下一圈落脚点
//顺时针
//循环的方向 -> 右下左上
for(int i = 0; i<4 ;i++){ //i控制方向
for(int j=0 ; j<full ; j++){//j根据次数填充
result[x][y]=num++;//填充
//移动当前该填充的位置
x+=dir[i][0];
y+=dir[i][1];
}
}
circle++;//一次顺时针结束,增加一圈
full = n-1-2*circle;//填充的数量随圈数发生变化
}
return result;
}
};