系列文章目录
刷题笔记(一)–数组类型:二分法
刷题笔记(二)–数组类型:双指针法
刷题笔记(三)–数组类型:滑动窗口
题录
54. 螺旋矩阵
先看题目截图:
注意,这道题和下道题的处理思路都是一样的,所以我这里把做法移动到了总结部分
public static List<Integer> spiralOrder(int[][] matrix) {
List<Integer> list = new ArrayList<>();
if(matrix == null || matrix.length == 0){
return list;
}
int left = 0;//定义左边界
int right = matrix[0].length - 1;//定义右边界
int top = 0;//定义上边界
int bottom = matrix.length - 1;//定义下边界
int count = matrix.length * matrix[0].length;//记录数组种类
//这里我的遍历思想是左闭右闭
while(count >= 1){
//先从左上到右上
for (int i = left; i <= right && count >= 1; i++) {
list.add(matrix[top][i]);
count--;
}
top++;
//再从右上到右下
for (int i = top; i <= bottom && count >= 1; i++) {
list.add(matrix[i][right]);
count--;
}
right--;
//再从右下到左下
for(int i = right;i >= left && count >= 1;i--){
list.add(matrix[bottom][i]);
count--;
}
bottom--;
//再从左下到左上
for (int i = bottom; i >= top && count >= 1; i--) {
list.add(matrix[i][left]);
count--;
}
left++;
}
return list;
}
59. 螺旋矩阵 II
还是老规矩,先看题
这道题的做法和上道题做法我都放在总结篇里面讲解。
public int[][] generateMatrix(int n) {
int[][] arr = new int[n][n];
int left = 0;//定义左边界
int right = n - 1;//定义右边界
int top = 0;//定义上边界
int bottom = n - 1;//定义下边界
int count = 1;//定义当前加入的数字
while(count <= n * n){
//首先从左上往右上遍历
for (int i = left; i <= right && count <= n*n; i++) {
arr[top][i] = count;
count++;
}
top++;
//然后是从右上往右下遍历
for (int i = top; i <= bottom && count <= n * n; i++) {
arr[i][right] = count;
count++;
}
right--;
//然后是从右下往左下遍历
for (int i = right; i >= left && count <= n * n; i--) {
arr[bottom][i] = count;
count++;
}
bottom--;
//然后是从左下往左上遍历
for (int i = bottom; i >= top && count <= n * n; i--) {
arr[i][left] = count;
count++;
}
left++;
}
return arr;
}
总结
可以看到,我这篇博客的命题叫做“模拟”,什么意思呢?也就是模拟输出。这种类型的题不会涉及什么算法,但是这个写代码题的过程却十分考验程序员对代码的细节处理。
那么我们要怎样螺旋遍历上述的数组呢?当然,肯定不能上去直接三七二十一就各种判断一顿操作,这样很快就把自己弄混了。做这种题一定要有条理性,也就是坚持循环不变量原则。什么意思呢?
这样解释:如果我们要顺时针螺旋遍历一个数组,怎么遍历?无非就是
上行从左到右
右列从上到下
下行从右到左
左列从下到上
这样我们就算是走完一圈了。可是问题就是在这里,我们边界很不明确呀,什么意思呢?我们看下面这个遍历
边界是不是很不明确,如果你还觉得明显,那再来
这样是不是闲的很乱?就很没有条理性,边界值就很不明确,哪里起步,哪里停止,临界限定条件是啥,一点都不清楚。那么一个条理清晰的遍历是怎样遍历的呢?
这个和上面两种遍历方式的差别是啥?可以发现下面这种的就很清晰。为什么清晰?
因为它的遍历条件很明确:每条边左闭右开。当前遍历取不到拐角处的方块,它会把每个拐角处的那个方块统一交给下次循环处理。
坚持每条边的遍历原则,我们就可以清晰完整的遍历整个数组。可是这个时候细心的读者就会发现了,我这里说的是左闭右开,但是我做题可不是这样搞得呀!我做题用的另外一种更为清晰的限制条件:每条边左闭右闭。什么意思呢?就是我每次遍历都会遍历完当前的行或者列,啥意思呢?看下图:
注意看我上面的方块的脱离顺序,每次都是当前一行或者当前一列的全部脱离,然后一点点缩小范围。这种的写法有什么好处呢?就是我们只需要控制边界值就好了,不要额外定义变量来进行遍历。更有利于我们去控制变量和理解代码思想。