像蛇一样打印矩阵

Printing a matrix like a snake

我要像蛇一样在 C# 中打印一个矩阵,类似这样的东西。关于如何进行的任何建议?

1 2 3 4 5 6 
          6
1 2 3 4   6
1     4   6
1     4   6
1     4   6 
1   3 4   6
1         6
1 2 3 4 5 6

这是我目前的代码。基本上我需要弄清楚如何跳过行和列。

Console.WriteLine("Enter your number: ");
        int n = Convert.ToInt32(Console.ReadLine());
        int[,] a = new int[n, n];
        int printVal = 1;
        int c1 = 0, c2 = n - 1;
        while(printVal <= n * n)
        {
            //dvizenje na desno
            for (int i = c1; i <= c2; i++)
                a[c1, i] = printVal++;

            //dvizenje nadolu
            for (int j = c1 + 1; j <= c2; j++)
                a[j, c2] = printVal++;

            // dvizenje na levo
            for (int i = c2 - 1; i >= c1; i--)
                a[c2, i] = printVal++;

            //dvizenje nagore
            for (int j = c2 - 1; j >= c1 + 1; j--)
                a[j, c1] = printVal++;
            c1++;
            c2--;
        }
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < n; j++)
            {
                Console.Write(a[i, j] + "\t");
            }
            Console.WriteLine();
        }

类似的东西,它还没有完成,可以更有效,但它是想法(Builder Design Pattern)

public class NumberElement
{
    private StringBuilder sb;

    public NumberElement()
    {
        sb = new StringBuilder();
    }

    public void AddNumbers(int Numbers)
    {
        for(int i = 1; i < Numbers + 1; i++)
        {
            AddNumber(i);
        }
    }

    public void AddSpaces(int SpaceNumber)
    {
        for (int i = 1; i < SpaceNumber + 1; i++)
        {
            AddSpace();
        }
    }
    public void AddNumber(int number)
    {
            sb.Append(number + " ");
    }
    public void AddSpace()
    {
            sb.Append("  ");
    }

    public void AddNewLine()
    {
        sb.Append("\n");
    }

    public override string ToString()
    {

        return sb.ToString();
    }
}

class Program
{
    static public void Example(NumberElement ne)
    {
        //Line 1
        ne.AddNumbers(6);
        ne.AddNewLine();
        //Line 2
        ne.AddSpaces(5);
        ne.AddNumber(6);
        ne.AddNewLine();
        //Line 3
        ne.AddNumbers(4);
        ne.AddSpace();
        ne.AddNumber(6);
        ne.AddNewLine();
        //Line 4
        ne.AddNumber(1);
        ne.AddSpaces(2);
        ne.AddNumber(4);
        ne.AddSpace();
        ne.AddNumber(6);
        ne.AddNewLine();
        //Line 5
        ne.AddNumber(1);
        ne.AddSpaces(2);
        ne.AddNumber(4);
        ne.AddSpace();
        ne.AddNumber(6);
        ne.AddNewLine();
        //Line 6
        ne.AddNumber(1);
        ne.AddSpaces(2);
        ne.AddNumber(4);
        ne.AddSpace();
        ne.AddNumber(6);
        ne.AddNewLine();
        //Line 6
        ne.AddNumber(1);
        ne.AddSpace();
        ne.AddNumber(3);
        ne.AddNumber(4);
        ne.AddSpace();
        ne.AddNumber(6);
        ne.AddNewLine();
        //Line 7
        ne.AddNumber(1);
        ne.AddSpaces(4);
        ne.AddNumber(6);
        ne.AddNewLine();
        //Line 8
        ne.AddNumbers(6);
    }

    static void Main(string[] args)
    {
        NumberElement ne = new NumberElement();
        Example(ne);
        Console.WriteLine(ne);
    }
}

作为快速练习,我制作了完整版本。如果有人感兴趣,这里是:

enum Directions { Right, Down, Left, Up }

Dictionary<Directions, Point> moves = new Dictionary<Directions, Point>
{
    { Directions.Right, new Point(1, 0) },
    { Directions.Down,  new Point(0, 1) },
    { Directions.Left,  new Point(-1,0) },
    { Directions.Up,    new Point(0,-1) },
};

int?[,] MakeSpiral(int rows, int columns)
{
    int?[,] field = new int?[rows, columns];

    Point current = new Point(0, 0);
    Directions direction = Directions.Right;

    while(true)
    {
        field[current.Y, current.X] = current.X + 1;
        Point next = current + moves[direction];

        if(!IsFieldPositionValid(next, current))
        {
            // If we can't make a move, change direction clockwise
            direction = (Directions)((int)(direction + 1) % 4);
        }

        next = current + moves[direction];

        if(!IsFieldPositionValid(next, current))
        {
            // If we can't make a move after changing the direction
            // it means that we are stuck and we completed the spiral
            return field;
        }

        current = next;
    }

    // Checks whether we can put a value in a given position
    // taking into consideration whether it is out of bounds of the field 
    // and if it's too close to other values
    bool IsFieldPositionValid(Point position, Point previous)
    {
        if(IsFieldPositionOutOfBounds(position))
            return false;

        if(!CheckSurroundings(position, point => !field[point.Y, point.X].HasValue, previous))
            return false;

        return true;
    }

    // Checks the positions in 4 directions using the isValid predicate.
    // Doesn't check the point that is given as the exclude parameter:
    // this is used so that we don't count the point we just moved from
    bool CheckSurroundings(Point position, Predicate<Point> isValid, Point? exclude = null)
    {
        foreach(Point move in moves.Values)
        {
            Point newPosition = position + move;
            if(IsFieldPositionOutOfBounds(newPosition))
                continue;
            if(!isValid(newPosition) && (!exclude.HasValue || exclude.Value != newPosition))
                return false;
        }

        return true;
    }

    bool IsFieldPositionOutOfBounds(Point position)
    {
        return position.Y >= field.GetLength(0) || position.X >= field.GetLength(1) || position.Y < 0 || position.X < 0;
    }    
}

并打印结果:

var spiral = MakeSpiral(9, 6);
for(int i = 0; i < spiral.GetLength(0); i++)
{
    for(int j = 0; j < spiral.GetLength(1); j++)
    {
        Console.Write(spiral[i, j]?.ToString() ?? " ");
        Console.Write(" ");
    }
    Console.WriteLine();
}

输出:

1 2 3 4 5 6 
          6 
1 2 3 4   6 
1     4   6 
1     4   6 
1     4   6 
1   3 4   6 
1         6 
1 2 3 4 5 6 

它不适用于大于 11 的值。下一个版本,稍微复杂一些,可以工作:

var spiral = Spiral(14, 24);
for(int i = 0; i < spiral.GetLength(0); i++)
{
    for(int j = 0; j < spiral.GetLength(1); j++)
    {
        Console.Write(spiral[i, j]?.ToString() ?? new string(' ', (j + 1).ToString().Length));
        Console.Write(" ");
    }
    Console.WriteLine();
}

输出:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 
                                                            24 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22    24 
1                                                     22    24 
1   3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20    22    24 
1   3                                           20    22    24 
1   3   5 6 7 8 9 10 11 12 13 14 15 16 17 18    20    22    24 
1   3   5                                 18    20    22    24 
1   3   5                                       20    22    24 
1   3   5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20    22    24 
1   3                                                 22    24 
1   3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22    24 
1                                                           24 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 

它需要来自某处的 Point 结构,如果您不想导入任何依赖项,我也为此做了一个基本实现:

struct Point : IEquatable<Point>
{
    public int X { get; set; }
    public int Y { get; set; }
    public Point(int x, int y) { X = x; Y = y; }

    public static Point operator +(Point left, Point right) => new Point(left.X + right.X, left.Y + right.Y);
    public static Point operator -(Point point) => new Point(-point.X, -point.Y);
    public static Point operator -(Point left, Point right) => left + -right;
    public static bool operator ==(Point left, Point right) => left.Equals(right);
    public static bool operator !=(Point left, Point right) => !left.Equals(right);

    public bool Equals(Point point) => this.X == point.X && this.Y == point.Y;
    public override bool Equals(object other) => other is Point p ? Equals(p) : false;
    public override int GetHashCode() => unchecked(17 * (X + 31 * Y));
}