这几天在看帖时看到比较多的面试题,其中有一个是螺旋矩阵的编程题,没事就用C++写了一个。所谓螺旋矩阵,就是从矩阵的第一行第一列开始,沿着某个方向用不断增加的数值依次填充矩阵,遇到边界或已填充的值则进行转向,直到将整个矩阵填满。如下是维数为6的螺旋矩阵:
1 2 3 4 5 6
20 21 22 23 24 7
19 32 33 34 25 8
18 31 36 35 26 9
17 30 29 28 27 10
16 15 14 13 12 11
编写这样的程序时,我觉得最主要的就是一个状态的判断,也就是一个简单的状态机:首先沿着某个方向(如向右),如果下个位置是合法且未被填充,则向着这个方向前进,否则进行转向。由于每个方向都基本上是一样的操作,故抽象出一个方向类Direction作为基类,提供 MoveForward 和 TurnRight 两个虚函数接口。派生出 ToEast 、ToSouth 、ToWest、ToNorth 四个子类,这四个子类实现自己的这两个方法,我只需要一个 Direction 类的指针就可以操作沿这四个方向的增长。这里提供的是顺时针旋转,如果愿意也可以逆时针旋转。
提供一个ScrewMatrix的类,作为生成螺旋矩阵。由于螺旋矩阵从1开始填充,矩阵为方阵,故设置的退出条件为填充完矩阵维数的平方个数值为止。代码如下(主要实现功能,规范性、安全性等另说):
ScrewMatrix.h:
#ifndef __ScrewMatrix_H_
#define __ScrewMatrix_H_
class ScrewMatrix;
class Direction
{
public:
virtual void MoveForward(ScrewMatrix* matrix) = 0;
virtual void TurnRight(ScrewMatrix* matrix) = 0;
};
class ToEast : public Direction
{
public:
void MoveForward(ScrewMatrix* matrix);
void TurnRight(ScrewMatrix* matrix);
};
class ToSouth : public Direction
{
public:
void MoveForward(ScrewMatrix* matrix);
void TurnRight(ScrewMatrix* matrix);
};
class ToWest : public Direction
{
public:
void MoveForward(ScrewMatrix* matrix);
void TurnRight(ScrewMatrix* matrix);
};
class ToNorth : public Direction
{
public:
void MoveForward(ScrewMatrix* matrix);
void TurnRight(ScrewMatrix* matrix);
};
class ScrewMatrix
{
public:
ScrewMatrix(int dir);
~ScrewMatrix();
public:
int GetCurXPos() { return m_x; }
int GetCurYPos() { return m_y; }
void SetCurXPos(int x) { m_x = x; }
void SetCurYPos(int y) { m_y = y; }
void ChangeDir(Direction* dir);
bool IsPosValiable();
bool IsFinished();
void Generate();
void Show();
private:
int m_Div;
int m_Num;
int m_Count;
int m_x;
int m_y;
int* m_Matrix;
Direction* m_Dir;
};
#endif
ScrewMatrix.cpp:
#include "stdafx.h"
#include "ScrewMatrix.h"
#include <assert.h>
#include <iostream>
using namespace std;
void ToEast::MoveForward(ScrewMatrix *matrix)
{
matrix->SetCurXPos(matrix->GetCurXPos() + 1);
}
void ToEast::TurnRight(ScrewMatrix* matrix)
{
matrix->ChangeDir(new ToSouth);
}
void ToSouth::MoveForward(ScrewMatrix *matrix)
{
matrix->SetCurYPos(matrix->GetCurYPos() + 1);
}
void ToSouth::TurnRight(ScrewMatrix* matrix)
{
matrix->ChangeDir(new ToWest);
}
void ToWest::MoveForward(ScrewMatrix *matrix)
{
matrix->SetCurXPos(matrix->GetCurXPos() - 1);
}
void ToWest::TurnRight(ScrewMatrix* matrix)
{
matrix->ChangeDir(new ToNorth);
}
void ToNorth::MoveForward(ScrewMatrix *matrix)
{
matrix->SetCurYPos(matrix->GetCurYPos() - 1);
}
void ToNorth::TurnRight(ScrewMatrix* matrix)
{
matrix->ChangeDir(new ToEast);
}
ScrewMatrix::ScrewMatrix(int dir)
{
assert(dir >= 1);
m_Div = dir;
m_x = 0;
m_y = 0;
m_Num = 0;
m_Count = m_Div * m_Div;
m_Dir = new ToEast;
m_Matrix = new int[m_Count];
assert(m_Dir != NULL);
assert(m_Matrix != NULL);
for(int i = 0 ; i < m_Div ; ++i)
for(int j = 0 ; j < m_Div ; ++j)
m_Matrix[i * m_Div + j] = 0;
}
ScrewMatrix::~ScrewMatrix()
{
if(m_Dir)
delete m_Dir;
if(m_Matrix)
delete m_Matrix;
}
void ScrewMatrix::ChangeDir(Direction* dir)
{
assert(dir != NULL);
if(m_Dir)
delete m_Dir;
m_Dir = dir;
}
bool ScrewMatrix::IsPosValiable()
{
if(m_x < 0 || m_x >= m_Div || m_y < 0 || m_y >= m_Div)
return false;
else
return true;
}
bool ScrewMatrix::IsFinished()
{
if(m_Num >= m_Count)
return true;
else
return false;
}
void ScrewMatrix::Generate()
{
int x,y;
while(! IsFinished())
{
while(IsPosValiable() && m_Matrix[m_y * m_Div + m_x] == 0)
{
m_Matrix[m_y * m_Div + m_x] = ++m_Num;
x = m_x;
y = m_y;
m_Dir->MoveForward(this); //最后会进入一个不可用位置而退出循环
}
m_x = x; //回退到上一个位置
m_y = y;
m_Dir->TurnRight(this); //转向并向前到一个可用位置
m_Dir->MoveForward(this);
}
}
void ScrewMatrix::Show()
{
for(int i = 0 ; i < m_Div ; ++i)
{
for(int j = 0 ; j < m_Div ; ++j)
{
cout<<m_Matrix[i * m_Div + j]<<" ";
}
cout<<endl;
}
}
测试:
#include "stdafx.h"
#include "ScrewMatrix.h"
int _tmain(int argc, _TCHAR* argv[])
{
ScrewMatrix matrix(6);
matrix.Generate();
matrix.Show();
return 0;
}