旋转外部N x N方阵,同时保留内部方阵不动

时间:2015-08-21 20:18:05

标签: c# multidimensional-array

如何旋转(或移动)2D正方形阵列,但是保留不在阵列“边界”上的任何元素?

示例:

1  2  3  4
12 13 14 5
11 15 16 6
10  9  8 7

我想要的是什么:

12  1  2 3
11 13 14 4
10 15 16 5
 9  8  7 6

这只是一个例子。我需要这个工作在N宽度的任何方形阵列上,其中2< N< 100

3 个答案:

答案 0 :(得分:2)

如果数组存储为等级为2的方形.NET数组,则代码非常简单,主要由四个循环组成 - 每个边一个:

var array = new [,] {
  {  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 }
};

// Rank = 2 and "square" and side length >= 2 checks removed for clarity.
var sideLength = array.GetLength(0);
// Save first element from top edge.
var element00 = array[0, 0];
// Rotate left edge.
for (var y = 1; y < sideLength; y += 1)
  array[y - 1, 0] = array[y, 0];
// Rotate bottom edge.
for (var x = 1; x < sideLength; x += 1)
  array[sideLength - 1, x - 1] = array[sideLength - 1, x];
// Rotate right edge.
for (var y = sideLength - 2; y >= 0; y -= 1)
  array[y + 1, sideLength - 1] = array[y, sideLength - 1];
// Rotate top edge.
for (var x = sideLength - 2; x > 0; x -= 1)
  array[0, x + 1] = array[0, x];
// Put saved element in place.
array[0, 1] = element00;

现在原始数组已按照问题中的描述进行了旋转。

如果数组存储为一维数组,则更容易创建一个类来执行旋转。该类可以存储可以跨方法调用使用的属性(数组和边长),以简化代码。

四个循环包含相同的逻辑,即使它们看起来不同:

class ArrayRotator<T> {

  public ArrayRotator(T[] array) {
    if (array == null)
      throw new ArgumentNullException("array");
    var sideLength = (Int32) Math.Sqrt(array.Length);
    if (sideLength*sideLength != array.Length)
      throw new ArgumentException("Not a square.", "array");
    Array = array;
    SideLength = sideLength;
  }

  public T[] Array { get; private set; }

  public Int32 SideLength { get; private set; }

  public void RotateArray() {
    if (SideLength < 3)
      return;
    // Save first element from top edge.
    var element00 = Array[0];
    // Rotate left edge.
    for (var y = 1; y < SideLength; y += 1)
      CopyElement(0, y, 0, y - 1);
    // Rotate bottom edge.
    for (var x = 1; x < SideLength; x += 1)
      CopyElement(x, SideLength - 1, x - 1, SideLength - 1);
    // Rotate right edge.
    for (var y = SideLength - 2; y >= 0; y -= 1)
      CopyElement(SideLength - 1, y, SideLength - 1, y + 1);
    // Rotate top edge.
    for (var x = SideLength - 2; x > 0; x -= 1)
      CopyElement(x, 0, x + 1, 0);
    // Put saved element in place.
    Array[1] = element00;
  }

  void CopyElement(Int32 x1, Int32 y1, Int32 x2, Int32 y2) {
    Array[GetIndex(x2, y2)] = Array[GetIndex(x1, y1)];
  }

  Int32 GetIndex(Int32 x, Int32 y) {
    return y*SideLength + x;
  }

}

以下是如何使用该类:

var array = new [] {
   1,  2,  3,  4,
  12, 13, 14,  5,
  11, 15, 16,  6,
  10,  9,  8,  7
};
var arrayRotator = new ArrayRotator<Int32>(array);
arrayRotator.RotateArray();

现在原始数组已按照问题中的描述进行了旋转。

答案 1 :(得分:1)

我发现这个问题非常有趣和有趣,我设计了一个小解决方案,只要阵列符合完美的方形,无论边长都适用,这应该有效。

注意:这不是最优化的解决方案(我很想尝试制作更优雅的解决方案,但现在这已经足够了)。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ArraySquareRotation
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] arr = new int[]
            {
                1, 2, 3, 4, 5, 3,
                12, 13, 14, 5, 6, 1,
                11, 15, 16, 6, 7, 22,
                10, 9, 8, 7, 8, 30,
                11, 15, 16, 6, 7, 22,
                1, 2, 3, 4, 5, 3
            };


            // detect array size
            int size = arr.Length;

            // calculate the side length of the array (in terms of index)
            int sideLength = BruteForceSquareDimensions(size);

            // find the start of the last side of the square
            int lastRowStartIndex = size - sideLength;

            // a placeholder for us to generate a shifted array
            int[] arrRotated = new int[size];


            Console.WriteLine("Detected square with side length {0}", sideLength);
            Console.WriteLine();

            for (int i = 1; i <= size; i++)
            {
                // side rotation
                if ((i % sideLength) == 0 && i != size)
                {
                    // is multiple of
                    // right hand side, shift down
                    arrRotated[i + sideLength - 1] = arr[i - 1];

                    // left hand side, shift up
                    arrRotated[i + sideLength - (sideLength * 2)] = arr[i];
                } else if (i < sideLength)
                {
                    int lastRowIndex = sideLength * (sideLength - 1);

                    // first row, shift right
                    arrRotated[i] = arr[i - 1];

                    // last row, shit left
                    arrRotated[i + lastRowIndex - 1] = arr[i + lastRowStartIndex];
                } else if(i < lastRowStartIndex)
                {
                    // the inner square (this case may need some work)
                    arrRotated[i - 1] = arr[i - 1];
                }

            }            

            Console.WriteLine("Printing original array");
            Console.WriteLine("======================");
            PrintSquareArray(arr);

            Console.WriteLine();


            Console.WriteLine("Printing Shifted array");
            Console.WriteLine("======================");
            PrintSquareArray(arrRotated);

            Console.WriteLine("Press any key to exit");
            Console.ReadKey();

        }

        /// <summary>
        /// there is definately a better way.
        /// </summary>
        /// <param name="size"></param>
        /// <returns></returns>
        static int BruteForceSquareDimensions(int size)
        {

            int sideLength = 0;

            for (int i = 1; i < size; i++)
            {
                if ((i * i) == size)
                {
                    sideLength = i;
                }
            }

            return sideLength;
        }

        /// <summary>
        /// This method just prints the array in the desired readable format
        /// </summary>
        /// <param name="arr"></param>
        static void PrintSquareArray(int[] arr)
        {
            int size = arr.Length;
            int sideLength = BruteForceSquareDimensions(size);

            for(int i = 1; i <= size; i++)
            {
                if ((i % sideLength) == 0)
                {
                    Console.WriteLine(arr[i - 1]);
                }
                else
                {
                    Console.Write(arr[i - 1] + "\t");
                }
            }
        }
    }
}

输出应如下所示(Square):

Detected square with side length 4

Printing original array
======================
1       2       3       4
12      13      14      5
11      15      16      6
10      9       8       7

Printing Shifted array
======================
12      1       2       3
11      13      14      4
10      15      16      5
9       8       7       6
Press any key to exit

这是一个6乘6

Detected square with side length 6

Printing original array
======================
1       2       3       4       5       3
12      13      14      5       6       1
11      15      16      6       7       22
10      9       8       7       8       30
11      15      16      6       7       22
1       2       3       4       5       3

Printing Shifted array
======================
12      1       2       3       4       5
11      13      14      5       6       3
10      15      16      6       7       1
11      9       8       7       8       22
1       15      16      6       7       30
2       3       4       5       3       22
Press any key to exit

答案 2 :(得分:1)

这就是我得到的。在您提供的4x4和以下5x5方阵列上进行测试。另外,我假设数据存储在锯齿状数组中,这样1个数组包含n个长度为n的数组。

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

static int[][] Transform(int[][] old)
{
    var maxIdx = old.Length-1;
    var canvas = new List<int[]>();

    //top border
    var top = new List<int>();
    top.Add(old[1][0]);
    for (var i = 0; i < maxIdx; i++)
    {
        top.Add(old[0][i]);
    }

    //bottom border
    var bottom = new List<int>();
    for (var i = 1; i < maxIdx+1; i++)
    {
        bottom.Add(old[maxIdx][i]);
    }
    bottom.Add(old[maxIdx - 1][maxIdx]);

    //middle
    var middle = new List<int[]>();
    for (var i = 1; i < maxIdx; i++) //for each inner array
    {
        var inside = new List<int>();
        inside.Add(old[i + 1][0]);
        for (var j = 1; j < maxIdx; j++)
        {
            inside.Add(old[i][j]);
        }
        inside.Add(old[i - 1][maxIdx]);
        middle.Add(inside.ToArray());
    }

    //Rebuild
    canvas.Add(top.ToArray());
    foreach (var arr in middle)
    {
        canvas.Add(arr);
    }
    canvas.Add(bottom.ToArray());
    return canvas.ToArray();
}