我保证是一个完美的方阵。我想从矩阵的中心开始,在这种情况下它将是
matrix[2][2]
,我知道如何计算中心(int)(dimensions / 2)
。我需要以下面的向外螺旋模式输出数组的内容。当然,该算法应该适用于任何完美的方阵。我不确定这个算法是否已经存在,我不想重新发明轮子。
int dimensions / 2;
21 22 23 24 25
20 7 8 9 10
19 6 1 2 11
18 5 4 3 12
17 16 15 14 13
此示例的输出应为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
让我们先识别模式..
偶数尺寸方阵,示例:4x4
07 > 08 > 09 > 10
^ v
06 (01)> 02 11
^ v v
05 < 04 < 03 12
v
[16]< 15 < 14 < 13
Starting Point: [2, 2] ~ [SIZE/2, SIZE/2]
Ending Point: [4, 1] ~ [SIZE, 1]
Chains: Count(K-chain)=2 for K = 1..(SIZE-2)
+ 3 for Count = SIZE-1
奇数尺寸方阵,示例:5x5
21 > 22 > 23 > 24 >[25]
^
20 07 > 08 > 09 > 10
^ ^ v
19 06 (01)> 02 11
^ ^ v v
18 05 < 04 < 03 12
^ v
17 < 16 < 15 < 14 < 13
Starting Point: [2, 2] ~ [⌊SIZE/2⌋, ⌊SIZE/2⌋]
Ending Point: [1, 5] ~ [1, SIZE]
Chains: Count(K-chain)=2 for K = 1..(SIZE-2)
+ 3 for Count = SIZE-1
void print_spiral (int ** matrix, int size)
{
int x = 0; // current position; x
int y = 0; // current position; y
int d = 0; // current direction; 0=RIGHT, 1=DOWN, 2=LEFT, 3=UP
int c = 0; // counter
int s = 1; // chain size
// starting point
x = ((int)floor(size/2.0))-1;
y = ((int)floor(size/2.0))-1;
for (int k=1; k<=(size-1); k++)
{
for (int j=0; j<(k<(size-1)?2:3); j++)
{
for (int i=0; i<s; i++)
{
std::cout << matrix[x][y] << " ";
c++;
switch (d)
{
case 0: y = y + 1; break;
case 1: x = x + 1; break;
case 2: y = y - 1; break;
case 3: x = x - 1; break;
}
}
d = (d+1)%4;
}
s = s + 1;
}
}
因为这闻起来像家庭作业,所以没有代码或直接答案,而只是一些提示:
您可以将其视为海龟问题:
令
m
为 1
细胞的运动,r
为沿螺旋方向(顺时针或逆时针)旋转 90
度。那么螺旋可以被编码为一系列形成此模式的海龟命令(从起点开始):
m,r,m,r,
m,m,r,m,m,r,
m,m,m,r,m,m,m,r,
m,m,m,m,r,m,m,m,m,r,
m,m,m,m,m,r
正如您所看到的,您从 1x 移动开始,然后在两次重复后旋转,切换到 2x 移动,2 次移动后切换到 3x 移动,...等等。这只需几个 for 循环即可完成(或者只需进行一些适当的迭代,并在击中单元格的矩阵数量时停止......或击中端点角)
您需要处理偶数/奇数矩阵大小
对于奇数矩阵大小来说,中间点很容易。对于偶数尺寸,情况会稍微复杂一些。如果使用顺时针旋转,则使用将大小减半的向下舍入除法结果,并开始向右移动。 (如果您需要不同的螺旋,则需要将
+1
添加到 x
和/或 y
并更改起始方向),以便螺旋保持居中。
因此,如果你得到偶数大小的矩阵,那么最后一次移动是两次,如果你得到奇数大小,那么最后一次移动只有一次(就像在这个例子中一样)
旋转
将方向存储为 2D 矢量。例如
d=(+1,0)
表示正确。要旋转 2D 矢量,您只需交换坐标并取反一个轴(这意味着 CW/CCW)。例如(x,y) -> (y,-x)
运动
也将当前位置存储为 2D 矢量。运动只是将当前方向矢量添加到其中。
享受解决这个问题的乐趣......
int radius = 0;
int i = centerX;
int j = centerY;
Debug.Log("i=" + i + "; j=" + j);
++i;
radius += 2;
while ((i < dimm) && (i >= 0))
{
for (int c = j; j < c + radius; j++)
Debug.Log("i=" + i + "; j=" + j);
--j;
--i;
for (int c = i; i > c - radius + 1; i--)
Debug.Log("i=" + i + "; j=" + j);
if (i < 0)
break;
else
Debug.Log("i=" + i + "; j=" + j);
--j;
for (int c = j; j > c - radius; j--)
Debug.Log("i=" + i + "; j=" + j);
++i;
++j;
for (int c = i; i < c + radius; i++)
Debug.Log("i=" + i + "; j=" + j);
radius += 2;
}
此代码将从自定义中心(CenterX,CenterY)输出逆时针平方矩阵(dimm X dimm)索引;如果超出矩阵大小,则结束。
bool IsIterationComplete(int iteration, int nCenter, std::vector<std::vector<bool>>& vVisited)
{
int nHigh = nCenter+iteration;
int nLow = nCenter-iteration;
//cout<<endl<<"High "<<nHigh<<"Low "<<nLow<<endl;
for(int i=nLow;i<=nHigh;i++)
{
if(!vVisited[nHigh][i] || !vVisited[nLow][i])
return false;
}
for(int i=nLow;i<=nHigh;i++)
{
if(!vVisited[i][nHigh] || !vVisited[i][nLow])
return false;
}
return true;
}
void PrintSpiral(std::vector<std::vector<int>>& vMat,std::vector<std::vector<bool>>& vVisited, int row, int col, int nCenter, int iteration)
{
cout<<vMat[row][col];
vVisited[row][col]=true;
if(row==0 && col==0)
return;
if(IsIterationComplete(iteration,nCenter,vVisited))
iteration++;
//cout<<endl<<"row "<<row<<" column "<<col<<"Iteration "<<iteration<<endl;
//left
if(col-1>=0 && !vVisited[row][col-1] && col-1>=nCenter-iteration)
{
cout<<"Left "<<endl;
PrintSpiral(vMat,vVisited,row,col-1,nCenter,iteration);
}
//Down
if((row+1)<vMat.size() && !vVisited[row+1][col] && row+1<=nCenter+iteration)
{
cout<<"Down "<<endl;
PrintSpiral(vMat,vVisited,row+1,col,nCenter,iteration);
}
//right
if((col+1)<vMat.size() && !vVisited[row][col+1] && col+1<=nCenter+iteration)
{
cout<<"Right "<<endl;
PrintSpiral(vMat,vVisited,row,col+1,nCenter,iteration);
}
//up
if(row-1>=0 && !vVisited[row-1][col] && row-1>=nCenter-iteration)
{
cout<<"Up "<<endl;
PrintSpiral(vMat,vVisited,row-1,col,nCenter,iteration);
}
}
int main (int argc, const char * argv[])
{
int nCount=1;
std::vector<std::vector<int>> vMat;
std::vector<std::vector<bool>> vVisited;
for(int i=0; i<7; i++)
{
std::vector<int> row;
std::vector<bool> visitedRow;
for(int j=0; j<7; j++)
{
row.push_back(nCount);
cout<<nCount<<'\t';
nCount++;
visitedRow.push_back(false);
}
cout<<'\n';
vMat.push_back(row);
vVisited.push_back(visitedRow);
}
cout<<'\n';
PrintSpiral(vMat,vVisited,vMat.size()/2,vMat.size()/2,vMat.size()/2,0);
return 0;
}
这是一个简单的Python解决方案:
def spiral_matrix(n):
# Create an n x n matrix filled with zeros
mat = [[0 for i in range(n)] for j in range(n)]
# Start from the middle of the matrix
x = n // 2
y = n // 2
# If n is even, adjust the start position
if n % 2 == 0:
x -= 1
y -= 1
# Initialize the first value to be filled into the matrix
val = 1
# Initialize the directions for movement (right, down, left, up)
dirs = [(0,1), (1,0), (0,-1), (-1,0)]
dir = 0 # Start with the first direction (right)
# Begin filling the matrix
while val <= n * n: # Continue until all cells are filled
# If the next cell is within the matrix and has not been visited
if 0 <= x < n and 0 <= y < n and mat[x][y] == 0:
# Fill the cell and increment the value
mat[x][y] = val
val += 1
else: # If the next cell is outside the matrix or already visited
# Move back to the previous cell and change the direction
x = px
y = py
dir -= 2 # Go back two steps in the direction sequence
# Save the current position
px = x
py = y
# Move to the next cell in the current direction
x += dirs[dir][0]
y += dirs[dir][1]
# Change the direction (right -> down -> left -> up -> right -> ...)
dir = (dir + 1) % 4
# Return the filled matrix
return mat
# Test the function with n = 5
n = 4
mat = spiral_matrix(n)
# Print the filled matrix
for row in range(n):
for col in range(n):
print(mat[row][col], end="\t")
print()
有一个基于 Khaled.K 的答案的 C# 版本:
/// <summary>
/// 从中心向外螺旋遍历
/// </summary>
/// <param name="matrix"></param>
/// <param name="action"></param>
/// <typeparam name="T"></typeparam>
private static void TraverseSpiralExpand<T>(T[,] matrix, Action<T> action)
{
var length = matrix.GetLength(0);
if (length != matrix.GetLength(1))
{
throw new InvalidDataException("SpiralExpand only supported on Square Matrix.");
}
// starting point
var x = (length - 1) / 2;
var y = (length - 1) / 2;
// 0=>x++, 1=>y++, 2=>x--, 3=>y--
var direction = 0;
action(matrix[x, y]);
for (var chainSize = 1; chainSize < length; chainSize++)
{
for (var j = 0; j < (chainSize < length - 1 ? 2 : 3); j++)
{
for (var i = 0; i < chainSize; i++)
{
switch (direction)
{
case 0:
x++;
break;
case 1:
y++;
break;
case 2:
x--;
break;
case 3:
y--;
break;
}
action(matrix[x, y]);
}
direction = (direction + 1) % 4;
}
}
}