最近听面试的同学考到了这道题,事先保存后打印的方法应该都会,或者搜一下有一大堆的,保存方向进行循环即可
但逐行打印的却较少,挺有趣也挺绕的
首先明确:设边长为n;
则最外圈有4 * (n - 1)个数;此外圈有4*(n - 3)个数,以此类推
因此可以先构造一个数组,保存到该圈之前,已有多少数字
此外,对于如下O(i, j)
所在位置,如何判断其属于第几个圈呢?
* * * * * *
* x * * * *
* * * * o *
* * * * * *
* * * * * *
直接看其距离边界的最小值:
距离最左端距离为4, 距离最顶端距离为2,最右端距离为1,最下端距离为2
因此说明其属于第二个圈,那么该圈的初始值为21(最外圈用了20个数)
此后判断其存在的数字:由于这个level=1=n-j
,由此判断从第二个圈的起始位置x
开始的那一行是填满的,接下来的一列填了j-1-level个数字(level表示所属的圈,这里是否-1或level从0开始还是从1开始都可以,但会影响这个算式)
同理若level=i-1,则说明这个数字在起始位置开始的那一行
若n-i=level,在从右往左的那一行
若j-1=level,在起始位置的那一列
由此即实现O(1)空间复杂度,O(n)时间复杂度的代码:
#include <iostream>
#include <cmath>
#include <iomanip>
using namespace std;
#define min(a, b) ((a) < (b)? (a) : (b))
void printMatrix(int n)
{
// 预先存下已用的数字数 (由于要求O(1)大小空间,因此这个数组后续未曾使用)
int *amount = new int[n / 2 + 1];
amount[0] = 0;
for (int i = 1; i <= n / 2; ++i)
amount[i] = amount[i - 1] + 4 * (n - 2 * i + 1);
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= n; ++j)
{
// 获得当前的点所在的圈数
int level = min(min(i - 1, j - 1), min(n - i, n - j));
// 该圈起始数字
// int initial = amount[level] + 1;
int initial = 4 * level * n + 1;
if (i - 1 == level) cout << setw(5) << initial + j - 1 - level;
else if (n - j == level) cout << setw(5) << initial + n - 2 * level - 1 + i - 1 - level;
else if (n - i == level) cout << setw(5) << initial + 2 * n - 4 * level - 2 + n - level - j;
else cout << setw(5) << initial + 3 * n - 6 * level - 3 + n - level - i;
}
cout << endl;
}
}
int main()
{
for (int n; cin >> n;)
printMatrix(n);
return 0;
}
输出:
5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
7
1 2 3 4 5 6 7
24 25 26 27 28 29 8
23 40 41 42 43 30 9
22 39 48 49 44 31 10
21 38 47 46 45 32 11
20 37 36 35 34 33 12
19 18 17 16 15 14 13