ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

蛇形矩阵全解析:对角线之字型、横向蛇形与螺旋矩阵的边界控制

蛇形矩阵全解析:对角线之字型、横向蛇形与螺旋矩阵的边界控制 今天聊一个绕不开的入门经典蛇形矩阵。这段时间在整理自己刷题笔记时发现这道题在不同平台、不同教材里的“蛇形”差别其实非常大很多人拿着一个版本的答案去解另一个版本结果越看越懵。更麻烦的是这种题看起来只需要两层循环实际操作中却非常容易栽在边界判断和方向切换上一错就是整片矩阵错位。这篇博文我会把蛇形矩阵最常见的对角线版本讲透再把横向之字型、螺旋回形这些兄弟版本一并对比给你一份可以直接照着打的完整方案。先亮一个观点蛇形矩阵考的不是背模板而是你对“方向控制”和“边界条件”的敏感度。顺着这个思路把本题彻底吃透之后螺旋矩阵、对角线遍历、矩阵旋转这些题都会轻松不少。1. 蛇形矩阵到底是什么动手前先把定义和边界理清楚1.1 不同题库里的三种“蛇形”定义不是开玩笑光是“蛇形矩阵”这个名字我在不同场合至少见过三种画法。第一种是横向之字型也叫S形填数从左上角开始往右填第一行从左到右第二行从右到左第三行再从左到右像蛇一样来回折返。这种形式在很多C语言入门教材里非常常见。第二种是斜向之字型也是本文的主角数字沿着矩阵对角线方向走第一次向右上方向第二次向左下方向两条相邻对角线方向交替一直填满整个n×n的方阵。这种定义在信息学竞赛、算法面试题单和在线评测系统里出现频率最高。第三种是螺旋回形也有人叫它蛇形矩阵数字从外圈开始绕圈顺时针由外向内一层层填进去更像回形针环绕的样子。我把三种方式在n3时的最终结果放到一起一眼就能看出区别类型3×3矩阵结果路径特点横向之字型1 2 3 / 6 5 4 / 7 8 9行方向来回交替对角线之字型1 2 6 / 3 5 7 / 4 8 9斜线方向成对交替螺旋回形1 2 3 / 8 9 4 / 7 6 5绕圈由外到内这里的差别是本质性的同样叫蛇形解法逻辑完全不同。所以看到题目的第一件事一定是确认题目里是否有样例输出没有的话要主动去找不要凭印象上手。1.2 对角线蛇形的核心规律抓住 ij 这条暗线以最常见的对角线之字型为例假设矩阵行列下标都从0开始那么每个格子(row, col)都有一个“斜线编号”就是 rowcol 的值。你会发现所有 sumrowcol 相同的格子刚好落在同一条从左下到右上的斜线上。比如4×4矩阵中sum0只有(0,0)一个格子sum1有(0,1)和(1,0)两个格子sum2有(0,2)、(1,1)、(2,0)三个格子依此类推sum从0到2n-2总共有2n-1条斜线。每条斜线的填充方向是交替的第0条向右上第1条向左下第2条向右上第3条向左下。你可以先在心里想象一下蛇爬行的姿态它先沿着右上方向爬到顶换一口气掉头向左下再爬到底再掉头向右上。对应到代码里就是两个方向向量不断切换右上方向row减1col加1左下方向row加1col减1只要把“斜线编号”和“方向奇偶”这两件事搞清楚之后所有实现都只是怎么把这句话翻译成代码的问题。2. 四种解法思路从模拟到规律逐层递进2.1 模拟法把矩阵当成蛇的活动地图模拟法是最直观的思路。你可以把每一格想象成贪吃蛇游戏里的一个节点蛇从(0,0)出发每填一个数字就朝当前方向走一格走出边界就换方向。对角线版本的模拟法需要维护两个方向状态当前是右上前进还是左下前进。每次尝试朝当前方向走下一步如果下一步越界不光要换方向还要找到一个合法的“对角线下一条斜线”的起始点。这个起始点通常在矩阵的某一条边或某一个角上选错了填数顺序就会乱。模拟法的优点是好理解调试的时候能顺着路径一点点看缺点是方向切换和越界处理混在一起稍不注意就写出很长的if分支而且容易漏掉“撞到角上”的特殊情况。所以如果你是想快速交作业模拟法可以如果想要代码简洁、不容易错我更推荐下面这种按斜线扫描的做法。2.2 对角线扫描把矩阵切成2n-1条斜线这个思路的核心是不再一格一格地模拟蛇怎么走而是直接按斜线编号顺序处理整条对角线。对于第s条斜线先判断它的方向如果s是偶数方向朝右上也就是row递减、col递增。起点在矩阵左侧边缘或者底部边缘。如果s是奇数方向朝左下也就是row递增、col递减。起点在矩阵顶部边缘或者右侧边缘。起点怎么算有一个很干净的小公式偶数斜线row s n ? s : n-1col s - row奇数斜线col s n ? s : n-1row s - col这里的逻辑不复杂。以4×4为例第4条斜线s4偶数应该从左侧或者底侧开始公式里row取到n-1也就是3col4-31起点就是(3,1)这正好是左下方向填完之后的转向点。所以整个算法可以写成“双重循环套一个while”的结构外层循环遍历2n-1条斜线内层循环沿着斜线一路填填出界就退出切到下一条斜线。这种方法代码量少逻辑清晰也不会出现模拟法里那种“走到死角不知道去哪”的问题我强烈建议用这种方式作为主力解法。2.3 坐标变换法把矩阵下标映射到斜线编号如果你不想用while循环一条线一条线地扫还有另一种等价做法直接根据斜线编号和方向用数学关系算出每个位置应该填哪个数。这个思路的本质是先离线构造一个“填数顺序数组”还是同一套起点和方向规律只不过把“while循环填充”换成了“for循环按顺序逐个赋值”。这样做的优点是公式化写代码的时候几乎不用想边界复制规律就行缺点是没有把“蛇形爬行”的直观过程体现出来新手理解起来稍微费劲。我还是以对角线扫描为主来展示代码因为它在“代码复杂度”和“可理解性”之间平衡得最好。2.4 横向之字型怎么处理一个if就能绕回来既然刚才提到了横向之字型这种更简单的蛇形矩阵这里也顺手讲一下它的解法因为很多朋友是看到标题“蛇形矩阵”就直接搜进来的结果平台上的题目其实是横向折返。横向之字型的规律很直白偶数行从左往右填奇数行从右往左填。实现时不需要维护方向数组只需要在每一行的for循环里判断行下标奇偶def snake_row_matrix(n): mat [[0] * n for _ in range(n)] num 1 for i in range(n): if i % 2 0: for j in range(n): mat[i][j] num num 1 else: for j in range(n - 1, -1, -1): mat[i][j] num num 1 return mat这段代码足够覆盖很多教材里“蛇形矩阵”的定义。如果你确认题目是横向折返版直接用这个版本就好不用费劲去写对角线扫描。这也是我为什么一直在强调看到题目先确认是哪一种蛇形再决定要不要上复杂解法。3. 手把手实操从手推4×4矩阵到完整可运行代码3.1 纸上推演n4的完整填数轨迹先别急着敲代码拿一张纸推一个4×4的例子比对着屏幕空想效率高很多。我们要求最终输出1 2 6 7 3 5 8 13 4 9 12 14 10 11 15 16我把自己草稿纸上的推演过程复述一遍顺序是这样的从(0,0)填1斜线编号0向右上方向但立刻出界所以下一条换成左下方向。斜线编号1取起点(0,1)填2向左下走到(1,0)填3出界结束。斜线编号2方向转回右上起点在左侧边缘(2,0)依次填4、5、6到(0,2)后出界。斜线编号3方向转回左下起点在顶部边缘(0,3)填7、8、9、10一路走到(3,0)出界。斜线编号4方向转回右上起点在底部边缘(3,1)填11、12、13到(1,3)出界。斜线编号5方向转回左下起点在右侧边缘(2,3)填14、15出界。斜线编号6方向转回右上起点在底部边缘(3,3)填16结束。这个过程一旦走通你会发现所有代码都是在做同一件事根据斜线编号求起点然后沿方向移动直到撞到矩阵边界。起点在哪个边缘完全由当前斜线编号和n的相对大小决定。3.2 Python完整实现20行搞定对角线蛇形直接给实现。在你本地跑一下输入4就能得到和上面一模一样的矩阵。def snake_diagonal_matrix(n): res [[0] * n for _ in range(n)] num 1 for s in range(2 * n - 1): if s % 2 0: # 右上方向row--, col r s if s n else n - 1 c s - r while r 0 and c n: res[r][c] num num 1 r - 1 c 1 else: # 左下方向row, col-- c s if s n else n - 1 r s - c while r n and c 0: res[r][c] num num 1 r 1 c - 1 return res if __name__ __main__: n int(input().strip()) mat snake_diagonal_matrix(n) for row in mat: print( .join(f{x:2d} for x in row))这段代码的关键点全在这两个起点公式里。写的时候我习惯先打印n2、n3、n4三个样例确认每条斜线的起点确实落在边界上。不要直接拿n5甚至更大的值去人肉验证那样出错了不好定位。3.3 C语言版本面向在线评测的写法很多在线练习平台要求用C或者C交题而且对输出格式更严格。我把C代码也写出来核心逻辑和Python版完全一致。#include stdio.h int main() { int n; scanf(%d, n); int mat[100][100] {0}; int num 1; for (int s 0; s 2 * n - 1; s) { if (s % 2 0) { int r (s n) ? s : n - 1; int c s - r; while (r 0 c n) { mat[r][c] num; r--; c; } } else { int c (s n) ? s : n - 1; int r s - c; while (r n c 0) { mat[r][c] num; r; c--; } } } for (int i 0; i n; i) { for (int j 0; j n; j) { if (j 0) printf( ); printf(%2d, mat[i][j]); } printf(\n); } return 0; }这里有一个我在在线评测中几乎必用的细节输出前先检查最后一行是不是多了空格。评测机对空格的容忍度差别很大有的题目末尾空格无所谓有的会把多出来的空格判成Presentation Error。我习惯在每行内部先判断“是不是这一行的第一个数”不是就用printf( )隔开这样无论评测机多严格都稳。4. 高频错误与排查清单为什么我的矩阵总是差一点4.1 数组越界起点算错就会瞬间爆出去最容易犯的错是把“斜线编号”和“实际行列索引”混在一起。比如s从0到2n-2循环有些人会习惯性写成range(2*n-1)没错但内部r和c的计算却套用了s的原始值。尤其当s超过n之后r或c必须由n-1来截断否则起点会落在矩阵范围之外。这种情况在本地跑小数据时经常不会立刻暴露因为数组溢出后未初始化的内存里恰好是0打印出来也只是看到几个0不会直接崩溃。等你把n调到100再开Debug模式才发现数组越界。所以我的习惯是所有下标变量的取值范围统一用一组不等式检查原理上保证最终写入的r和c永远在[0, n-1]之间而不是靠运气。4.2 方向判断写反偶数和奇数有点拧方向奇偶弄反是非常隐蔽的错。如果你把偶数斜线写成左下方向奇数斜线写成右上方向那么靠n2的样例根本看不出来因为两条对角线长度都很短。但n4时方向一错整个矩阵的填充顺序就变成了对称镜像数字排列完全不对。我建议你在写代码前先在注释里写清楚s是偶数右上r递减c递增s是奇数左下r递增c递减不要靠记忆硬写每次写代码前把这两行注释摆上去写完对照注释检查基本能规避一半以上的低级错误。4.3 输出格式对齐、空格和换行这个点其实非常影响实际体验。如果你在本地打印时只打印数字不控制宽度n超过9以后数字就会参差不齐很难用眼睛核对结果。如果你在在线评测时没有按行处理行尾空格也有可能被提示Presentation Error虽然题目本身算法没错但评分就是不给你过。我自己在本地测试时会先用%2d或者f{x:2d}做格式对齐一眼看出行列关系真正提交代码时再改成普通空格分隔去除不必要的填充。这样既能快速排查逻辑错误又不会因为格式问题在评测机上翻车。4.4 测试用例速查表从n0到n5有些题目n给的范围是非负整数甚至可能出现n0这种极端数据。很多人没有对n0做保护依然申请mat[0][0]或者循环里尝试访问空矩阵直接崩溃。我测试时一般会跑下面这一组数据n期望输出或说明0不输出任何内容程序正常结束1121 2 / 4 3注意对角线方向31 3 4 / 2 5 8 / 6 7 941 2 6 7 / 3 5 8 13 / 4 9 12 14 / 10 11 15 1651 2 6 7 15 / 3 5 8 14 17 / 4 9 13 18 22 / 10 12 19 21 23 / 11 20 24 25 26等下上面那个n5是按我提供的对角线方向推的不同写法可能有差异。别急着拿这个表当标准答案关键是理解规则每条斜线要么从左上到右下要么从右下到左上。如果是横向之字型规则n3会输出1 2 3 / 6 5 4 / 7 8 9两者完全不一样。所以再次提示测试前先确认你平台上的定义。5. 进阶扩展蛇形矩阵家族与面试考点一网打尽5.1 螺旋矩阵怎么用同样的思路迁移对角线蛇形写明白之后螺旋矩阵其实顺手就能拿下。螺旋矩阵的核心依然是方向控制区别在于它的方向是右下左上四个方向周期性切换每次撞墙或者碰到已经填过的格子就右转90度。用方向数组来写是最稳的def spiral_matrix(n): mat [[0] * n for _ in range(n)] dirs [(0, 1), (1, 0), (0, -1), (-1, 0)] d 0 r, c 0, 0 for val in range(1, n * n 1): mat[r][c] val nr, nc r dirs[d][0], c dirs[d][1] if not (0 nr n and 0 nc n and mat[nr][nc] 0): d (d 1) % 4 nr, nc r dirs[d][0], c dirs[d][1] r, c nr, nc return mat这段代码里的“下一步越界或者已经填过”就是转向触发条件和“蛇撞墙就换方向”是同一种思维模型。所以不要觉得蛇形矩阵是一道孤立题它其实是“方向控制类”矩阵题的地基。5.2 上三角蛇形、列方向蛇形等变体除了方阵有些题目会把蛇形玩出更多花样。我见过比较多的有上三角蛇形矩阵只输出矩阵的上三角部分第i行有n-i个元素斜线方向依然交替。还有列方向蛇形每一列从上到下还是从下到上交替填充本质就是横向之字型旋转90度之后的结果。这些变体都不建议当成新题背而是回到同一个分解思路先确定有哪几个方向再确定什么时候换方向最后确定每一段的起点落在哪个边界。只要这三件事理清楚写出来的代码基本不会跑偏。5.3 面试考察点与我的实战经验面试官如果出这道题最想看的不是你能不能默写代码而是你会不会先和面试官确认“蛇形”到底指哪一种。因为定义不同代码天差地别。你如果能反问一句“是横向蛇形还是斜向蛇形或者从外圈绕的螺旋形”面试官就知道你不是背题的人。我个人在实际刷题中的习惯是先花30秒在草稿纸上画n4的填充顺序把这个顺序写出来之后再开始写代码。别小看这30秒它能帮你确认起点公式、方向切换规则、边界截断方式全部正确节省的调试时间远远不止30秒。另一个小技巧是写完代码后把n从1到4全部跑一遍肉眼比对输出而不是只盯一个大样例。大样例出错很难定位小样例却可以精确指到某条斜线、某个起点、甚至某一次方向切换。这道题难吗认真捋一遍它其实只包含两件事斜线编号怎么算方向怎么换。把这两件事刻进脑子蛇形矩阵不但不会是障碍还会成为你理解所有矩阵遍历问题的助跑器。希望这篇拆解能让你下次遇到“蛇形矩阵”时直接一眼看穿它想要哪种蛇。
返回列表