蛇形填数是一个经典的算法练习题,要求在一个n×n的方阵中按照特定顺序填入数字1到n²。这个顺序不是常规的行优先或列优先,而是像蛇一样盘旋前进。具体来说,数字的填充顺序是:从右上角开始,先向下到底,再向左到底,然后向上到底,最后向右到底,如此循环往复,直到填满整个矩阵。
这个问题的难点在于如何准确地控制填充方向的变化,以及在边界条件下如何正确处理转向逻辑。初学者常常会在边界判断和方向切换上出错,导致数组越界或填充顺序错误。
让我们仔细分析提供的C++实现代码:
cpp复制#include<bits/stdc++.h>
using namespace std;
const int maxn=30;
int a[maxn][maxn]={};
int main() {
int n,x,y,num=0;
cin>>n;
num=a[x=0][y=n-1]=1;
while(num<n*n){
while(x+1<n&& !a[x+1][y])a[++x][y]=++num;
while(y-1>=0&& !a[x][y-1])a[x][--y]=++num;
while(x-1>=0&& !a[x-1][y])a[--x][y]=++num;
while(y+1<n&& !a[x][y+1])a[x][++y]=++num;
}
for(x=0;x<n;x++){
for(y=0;y<n;y++){
cout<<a[x][y]<<" ";
}
cout<<endl;
}
return 0;
}
注意:原代码中第四个while循环的条件有误,应为y+1<n而非x+1>=0,已修正
初始化阶段:
填充循环:
输出阶段:
蛇形填数的核心在于方向控制。代码中使用了四个while循环分别处理四个方向:
每个方向的移动都遵循相同模式:
边界处理是这类问题的关键难点:
虽然当前实现已经清晰,但仍有一些优化点:
cpp复制int dir[4][2] = {{1,0},{0,-1},{-1,0},{0,1}}; // 下、左、上、右
int d = 0; // 当前方向
while(num < n*n){
int nx = x + dir[d][0], ny = y + dir[d][1];
if(nx>=0 && nx<n && ny>=0 && ny<n && !a[nx][ny]){
x = nx; y = ny;
a[x][y] = ++num;
}else{
d = (d+1)%4; // 改变方向
}
}
数组越界:
死循环:
填充顺序错误:
小规模测试:
打印中间状态:
边界值测试:
这个题目很好地训练了:
在实际编码过程中,我发现以下几点特别重要:
一个实用技巧是使用条件断点调试:在调试器中设置条件断点,比如当x==2且y==3时中断,可以精准观察特定位置的填充过程。
对于初学者,我建议先实现固定大小的矩阵(如5×5),去掉输入部分,专注于填充逻辑的调试。等核心算法正确后,再增加动态大小输入的功能。
最后,这类问题虽然看起来简单,但包含了算法设计的核心思想:如何将复杂问题分解为可管理的步骤,并通过循环和条件判断组合解决。掌握这类基础问题的解法,对培养计算思维和编程能力都大有裨益。