旋转外部N x N个正方形数组,同时保持内部正方形数组不移动

本文关键字:数组 正方形 内部 移动 外部 旋转 | 更新日期: 2023-09-27 18:09:15

如何旋转(或移动)一个二维正方形数组,但留下任何元素,不在数组的"边界"未移动?

的例子:

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 x N个正方形数组,同时保持内部正方形数组不移动

如果数组存储为rank为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();

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

我发现这个问题很有趣,我设计了一个小解决方案,只要数组符合完全平方,这应该工作,无论边长

注意:这不是最优化的解决方案(我很想尝试做一个更优雅的解决方案,但这应该足够了)。

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");
                }
            }
        }
    }
}

输出应该如下所示(正方形):

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

这是我得到的。在您提供的4x4和以下5x5方形阵列上进行了测试。此外,我假设数据存储在锯齿数组中,这样一个数组包含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();
}