)
覆盖54. 螺旋矩阵、48. 旋转图像、240. 搜索二维矩阵 II。每题统一为① 题目分析与解题思路含常见出错点→② 常见解法及讲解→③ 知识点总结。54. 螺旋矩阵① 题目分析与解题思路题意给定 m 行 n 列矩阵顺时针螺旋遍历把元素按顺序放到一维 List 里返回。核心思路边界收缩法。定义四个边界上边界top、下边界bottom、左边界left、右边界right循环分 4 个方向一圈一圈遍历从左→右遍历 top 这一行遍历完 top上边界往下缩从上→下遍历 right 这一列遍历完 right–右边界往左缩从右→左遍历 bottom 这一行遍历完 bottom–下边界往上缩从下→上遍历 left 这一列遍历完 left左边界往右缩每走完一个方向后要判断边界是否已经交叉如果top bottom或者left right直接终止循环防止单行、单列矩阵重复读取。循环直到边界交叉所有元素读取完毕。常见出错点❌ 没有边界判断单行/单列矩阵走完左→右之后继续执行从上到下、从右到左造成重复取值❌ 边界更新顺序写错方向走完忘记收缩对应的边界❌ 循环终止条件写错只用top bottom忽略左右边界❌ 下标越界遍历行、列的时候循环条件写反比如j left写成j left❌ 方向顺序乱掉螺旋必须严格左→右 → 上→下 → 右→左 → 下→上顺序错直接结果错乱❌ 用 visited 标记数组可以做但额外空间不是最优② 常见解法及讲解解法 1边界收缩推荐空间 O(1)返回结果数组不算额外空间classSolution{publicListIntegerspiralOrder(int[][]matrix){ListIntegerresnewArrayList();if(matrixnull||matrix.length0)returnres;intmmatrix.length;intnmatrix[0].length;inttop0,bottomm-1;intleft0,rightn-1;while(topbottomleftright){// 1. 左 - 右顶行for(intjleft;jright;j){res.add(matrix[top][j]);}top;if(topbottom)break;// 2. 上 - 下右列for(intitop;ibottom;i){res.add(matrix[i][right]);}right--;if(leftright)break;// 3. 右 - 左底行for(intjright;jleft;j--){res.add(matrix[bottom][j]);}bottom--;if(topbottom)break;// 4. 下 - 上左列for(intibottom;itop;i--){res.add(matrix[i][left]);}left;if(leftright)break;}returnres;}}讲解时间复杂度O(mn)每个元素只会访问一次每次走完一个方向立刻判断边界解决单行、单列的特殊 case只使用 4 个边界变量辅助空间是 O(1)输出 list 不算算法额外空间解法 2标记访问法不推荐额外空间开一个同样大小的boolean[][] visited标记这个位置有没有读过配方向数组{{0,1},{1,0},{0,-1},{-1,0}}碰到边界或已访问的格子就切换方向。优点逻辑直观缺点额外 O(mn) 空间。classSolution{publicListIntegerspiralOrder(int[][]matrix){ListIntegerresnewArrayList();intmmatrix.length;// 行数intnmatrix[0].length;// 列数boolean[][]visitednewboolean[m][n];// 标记是否访问// 四个方向右、下、左、上int[][]dirs{{0,1},{1,0},{0,-1},{-1,0}};intx0,y0;// 起点坐标左上角(0,0)intdir0;// 初始方向向右// 一共 m*n 个元素循环m*n次for(intk0;km*n;k){// 1. 当前格子取值、标记已访问res.add(matrix[x][y]);visited[x][y]true;// 2. 计算【下一个坐标】沿着当前方向走一步intnxxdirs[dir][0];intnyydirs[dir][1];// 判断下一步能不能走// 不能走的条件nx/ny越界或者这个位置已经访问过if(nx0||nxm||ny0||nyn||visited[nx][ny]){// 顺时针换方向右→下→左→上循环dir(dir1)%4;// 换方向之后重新计算下一步nxxdirs[dir][0];nyydirs[dir][1];}// 3. 更新坐标走到下一步xnx;yny;}returnres;}}核心思想我们有4 个固定行进方向按顺序右 → 下 → 左 → 上循环轮换。每走到一个格子把值加入结果标记这个格子已经访问过先尝试沿着当前方向往前走一步如果下一步越界 或者 已经被访问过说明撞墙了顺时针切换下一个方向再往前走一共走m * n次矩阵总元素数量全部走完就结束高频易错点方向数组顺序写错必须是右→下→左→上顺序错螺旋直接乱掉换方向之后忘记重新计算 nx、ny很多人这里漏写直接 x,y 不变死循环循环次数写错不是 while直接 for 循环 m*n 次因为总元素固定比 while 更稳坐标 x、y 搞反x 是行、y 是列matrix[x][y]别写成matrix[y][x]③ 知识点总结两种写法一句话对比边界收缩不断把围墙往里收沿着围墙走一圈标记访问法自由行走走过的地方打标记撞墙就拐弯怎么选面试写边界收缩只动 4 个边界变量、空间 O(1)标记访问法更像人走路、方向数组好迁移但多 O(mn) 空间且换方向后必须重算nx、ny。48. 旋转图像① 题目分析与解题思路题目要点n×n 方阵原地顺时针旋转 90°不能新建一个同等大小矩阵存放结果。旋转坐标公式原位置(i,j)顺时针转 90° 之后的新位置(j, n-1-i)。但是直接按这个公式赋值会覆盖掉还没读取的值所以不能直接一次性赋值。核心思路两种主流原地方案方案 A先转置再每行反转最好写推荐优先掌握矩阵转置matrix[i][j] - matrix[j][i]对角线交换每一行左右翻转举例原矩阵 1 2 3 4 5 6 7 8 9 转置后 1 4 7 2 5 8 3 6 9 每行反转 7 4 1 8 5 2 9 6 3✅ 正好是顺时针 90° 结果。方案 B四层循环四个位置轮换交换一圈一圈处理外层环逐步向内层每一组 4 个点循环交换用临时变量保存一个值避免覆盖。四个点关系(i,j) → (j,n-1-i) → (n-1-i,n-1-j) → (n-1-j,i) → (i,j)常见出错点❌ 不理解原地要求直接新建二维数组存放结果不符合题目要求会被判错❌ 直接使用公式matrix[j][n-1-i] matrix[i][j]值被提前覆盖后面读到错误数据❌ 转置的时候循环范围写错如果 i,j 全部遍历会交换两次等于白转置转置只需要遍历上三角ji❌ 四个点轮换的时候下标写错行列搞反旋转方向不对变成逆时针❌ 分层循环边界错误奇数阶矩阵中心元素不用处理循环层数/每行处理个数写错❌ 反转行的时候写反变成每列反转结果错误② 常见解法及讲解解法 1转置 行反转简单好记面试首选classSolution{publicvoidrotate(int[][]matrix){intnmatrix.length;// 第一步矩阵转置只交换上三角for(inti0;in;i){for(intji1;jn;j){inttempmatrix[i][j];matrix[i][j]matrix[j][i];matrix[j][i]temp;}}// 第二步每一行左右翻转for(inti0;in;i){intl0,rn-1;while(lr){inttempmatrix[i][l];matrix[i][l]matrix[i][r];matrix[i][r]temp;l;r--;}}}}讲解转置沿着主对角线交换i是行j从i1开始避免重复交换行反转双指针一行里左右两头交换直到相遇时间复杂度O(n2)O(n^2)O(n2)每个元素被访问常数次空间复杂度O(1)O(1)O(1)原地解法 2四个位置轮换交换分层旋转classSolution{publicvoidrotate(int[][]matrix){intnmatrix.length;// 外层循环控制层数一共 n/2 层for(inti0;in/2;i){// 当前这一层需要处理的元素个数 n-2*i -1for(intji;jn-1-i;j){inttempmatrix[i][j];// 第四个点 → 第一个点matrix[i][j]matrix[n-1-j][i];// 第三个点 → 第四个点matrix[n-1-j][i]matrix[n-1-i][n-1-j];// 第二个点 → 第三个点matrix[n-1-i][n-1-j]matrix[j][n-1-i];// 暂存的temp → 第二个点matrix[j][n-1-i]temp;}}}}讲解一层一层向内圈处理一共n/2层每一组 4 个位置顺时针轮换先用 temp 保存第一个值防止覆盖丢失适合理解旋转几何关系下标容易写错面试优先推荐解法 1补充如果是逆时针旋转 90 度先转置再反转列或者等价别的变换。③ 知识点总结口诀顺时针 90° 转置 每行反转逆时针 90° 转置 每列反转。怎么选优先背【转置 行翻转】代码短、下标不易写错四个位置轮换更贴近几何关系但四个下标最容易写反写错就变成逆时针。奇数阶中心元素始终不动分层n/2层就够。240. 搜索二维矩阵 II① 题目分析与解题思路题目特点矩阵满足每行从左往右升序每列从上往下升序。⚠️ 和 74 题【搜索二维矩阵】不一样74 题是整行最后一个 下一行第一个本题没有这个性质不能直接当成一维有序数组二分。要求高效查找目标值 target返回是否存在。Z 字形搜索思路✅ 最优思路Z 字形搜索从右上角/左下角出发面试标准解法。选择起点右上角 (row0, coln-1)如果当前值 target找到返回 true如果当前值 target说明这一列下面所有数都更大左移一列col–如果当前值 target说明这一行左边所有数都更小下移一行row终止条件row 越界 或者 col 越界说明不存在返回 false为什么选右上角这个位置是一个分界点左边更小、下边更大每次可以直接排除一整行/一整列单向搜索不会有分支。左下角同理上面更小、右边更大只是判断反过来。常见出错点❌ 直接暴力遍历全部元素时间复杂度O(mn)O(mn)O(mn)不符合题目高效算法要求大数据会超时❌ 照搬 74 题的二分思路误以为每行末尾小于下一行开头直接一维二分逻辑错误❌ 起点选错左上角左上角元素右边大、下边也大target 比它大的时候不知道该往下还是往右无法缩小范围死路❌ 边界判断写错row、col 的增减写反循环条件row m col 0写错数组下标越界❌ 行列搞混matrix[row][col]写成matrix[col][row]❌ 单独对每一行依次二分虽然能过复杂度O(mlogn)O(m\log n)O(mlogn)可以通过但不是本题最优解法面试会被追问更优方案② 常见解法及讲解解法 1Z 字形搜索最优O(mn)O(mn)O(mn)推荐起点右上角classSolution{publicbooleansearchMatrix(int[][]matrix,inttarget){if(matrixnull||matrix.length0||matrix[0].length0){returnfalse;}intmmatrix.length;// 行数intnmatrix[0].length;// 列数introw0;intcoln-1;// 起点右上角while(rowmcol0){if(matrix[row][col]target){returntrue;}elseif(matrix[row][col]target){col--;// 当前值太大向左走舍弃当前列}else{row;// 当前值太小向下走舍弃当前行}}returnfalse;}}每次比较直接淘汰一整行或者一整列最多走 mn 步时间复杂度O(mn)O(mn)O(mn)空间O(1)O(1)O(1)右上角、左下角都是单分支选择点左上角、右下角两个方向都更大/更小无法唯一确定走哪边也可以改成左下角起点rowm-1, col0大于 target 就 row–小于 target 就 col起点左下角classSolution{publicbooleansearchMatrix(int[][]matrix,inttarget){if(matrixnull||matrix.length0||matrix[0].length0){returnfalse;}intmmatrix.length;// 行数intnmatrix[0].length;// 列数introwm-1;intcol0;// 起点左下角while(row0coln){if(matrix[row][col]target){returntrue;}elseif(matrix[row][col]target){row--;// 当前值太大向上走舍弃当前行}else{col;// 当前值太小向右走舍弃当前列}}returnfalse;}}起点条件 target条件 target右上角col-- 左移row 下移左下角row-- 上移col 右移解法 2逐行二分O(mlogn)O(m\log n)O(mlogn)次优容易写classSolution{publicbooleansearchMatrix(int[][]matrix,inttarget){for(int[]row:matrix){intleft0;intrightrow.length-1;while(leftright){// 逐行二分查找intmiddleleft(right-left)/2;if(row[middle]target){returntrue;}elseif(row[middle]target){rightmiddle-1;}else{leftmiddle1;}}}returnfalse;}}讲解利用每行有序每行单独二分查找代码简单但是性能不如 Z 字形搜索数据量大的时候差距明显③ 知识点总结只有一对角能做起点右上角往左变小、往下变大左下角反之。都是一个方向变小、另一个变大比较一次就能唯一确定方向左上角两边都变大、右下角两边都变小必然分叉。三种写法复杂度Z 字形O(mn)O(mn)O(mn)最优 逐行二分O(mlogn)O(m\log n)O(mlogn)能过非最优 暴力O(mn)O(mn)O(mn)超时。