Как повернуть двумерный массив?

Вдохновленный Сообщение Раймонда Чена, допустим, у вас есть двумерный массив 4x4, напишите функцию, которая поворачивает его на 90 градусов. Раймонд ссылается на решение в псевдокоде, но я хотел бы увидеть кое-что из реального мира.

[1][2][3][4]
[5][6][7][8]
[9][0][1][2]
[3][4][5][6]

Становится:

[3][9][5][1]
[4][0][6][2]
[5][1][7][3]
[6][2][8][4]

Обновлять: Ответ Ника самый простой, но есть ли способ сделать это лучше, чем n ^ 2? А если бы матрица была 10000х10000?

Как вы могли уйти с меньшим, чем n ^ 2? Все элементы должны быть прочитаны и установлены, а элементов n ^ 2

erikkallen 14.03.2009 22:06

См. Также stackoverflow.com/questions/848025/rotating-bitmaps-in-code

Marco van de Voort 15.05.2009 10:32

Задача этого вопроса (в рассказе Раймонда) заключалась в том, чтобы просто убедиться, что у кандидата есть определенный набор базовых способностей. Решение n ^ 2 в порядке.

i_am_jorf 03.11.2009 17:52

Какой твой? Вы не говорите, является ли 2D-массив квадратным (это не в общем случае! Например, вектор - это матрица с одним размером 1), но вы, кажется, подразумеваете, что n - это ширина и высота, и, следовательно, имеет n² элементов. . Было бы разумнее иметь n как количество элементов, где n = w × h.

niXar 07.01.2010 01:34

Согласен с niXar. То, что N сильно коррелирует с квадратом некоторого другого значения, не делает решение N ^ 2.

Merlyn Morgan-Graham 23.10.2010 09:19

У меня нет времени писать об этом, но предпочтительный способ выполнения вращения - использовать кватернионы.

Milimetric 05.04.2012 06:45

Было бы сложнее, если бы его размер был NxM вместо NxN.

Peter Lee 17.07.2013 05:12

Обратите внимание, что для больших массивов промахи в кеше могут быть проблематичными, поэтому вы захотите использовать тайлинг, как это предлагается в ответе на stackoverflow.com/questions/848025/rotating-bitmaps-in-code. В результате вы получите четырехкратный вложенный цикл.

Xantix 04.08.2013 00:10

Вот быстрый способ сделать это: сохранить индексы строки и столбца (скажем, i и j). Транспонирование занимает постоянное время (просто поменяйте индексы местами :). То же самое можно проделать и с поворотами (поиграйте с индексами).

mrk 07.10.2013 23:29

В случае, если n ^ 2 невозможно. Вы можете создать интерфейс для доступа к каждому элементу. Затем, учитывая (i, j), примените вращение к (i, j), получите доступ к повернутому элементу и вернитесь. Может быть, не лучшее решение, но работает.

Confuse 08.12.2016 08:04

Я думаю, что вы все еще можете сделать это меньше, чем N ^ 2. Однако требуется более сложная структура данных. Может быть, какой-то тип двусвязного списка?

Younes Nj 17.02.2018 02:47

@erikkallen может быть сжат?

Nathan 21.06.2018 02:25

Я сделал это одним циклом: stackoverflow.com/a/61812224/996926

advncd 15.05.2020 08:16
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
321
13
329 460
62
Перейти к ответу Данный вопрос помечен как решенный

Ответы 62

Ответ принят как подходящий

Вот он на C#

int[,] array = new int[4,4] {
    { 1,2,3,4 },
    { 5,6,7,8 },
    { 9,0,1,2 },
    { 3,4,5,6 }
};

int[,] rotated = RotateMatrix(array, 4);

static int[,] RotateMatrix(int[,] matrix, int n) {
    int[,] ret = new int[n, n];

    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            ret[i, j] = matrix[n - j - 1, i];
        }
    }

    return ret;
}

Конечно, но как насчет решения, использующего память O (1)?

AlexeyMK 07.09.2008 21:51

Ваше решение имеет сложность пространства O (n ^ 2). Нужно делать лучше

Kshitij Jain 06.10.2013 07:54

Как насчет матрицы N X M?

Rohit 08.08.2014 14:21

Сложность линейна по количеству элементов в массиве. Если N - количество элементов, сложность O (N). Если N - длина стороны, то да, сложность O (N ^ 2), но это все еще оптимально. Вы должны прочитать каждый элемент хотя бы один раз. Печать матрицы такая же сложность

Alejandro 04.08.2015 23:34

Здесь добавлен ответ для матрицы N x M: stackoverflow.com/a/38027015/1322703

Alexander Bekert 25.06.2016 12:37

@KshitijJain, как это сложность пространства O (n ^ 2)? Он дублирует массив, так разве это не O (n) пространство?

Teoman shipahi 30.07.2017 05:12

Для поворота на -90 градусов: ret[i][j] = matrix[j][n - i - 1]

Duncan Luk 06.08.2017 23:57

Поправьте меня, если я ошибаюсь, но разве параллелизм (при условии, что система имеет разумное количество ядер) не может уменьшить сложность, хотя бы немного?

James M 02.04.2018 23:17

Смотрите мой ответ одним циклом: stackoverflow.com/a/61812224/996926

advncd 15.05.2020 08:18

Вот моя версия Ruby (обратите внимание, что значения не отображаются одинаково, но она все равно вращается, как описано).

def rotate(matrix)
  result = []
  4.times { |x|
    result[x] = []
    4.times { |y|
      result[x][y] = matrix[y][3 - x]
    }
  }

  result
end

matrix = []
matrix[0] = [1,2,3,4]
matrix[1] = [5,6,7,8]
matrix[2] = [9,0,1,2]
matrix[3] = [3,4,5,6]

def print_matrix(matrix)
  4.times { |y|
    4.times { |x|
      print "#{matrix[x][y]} "
    }
    puts ""
  }
end

print_matrix(matrix)
puts ""
print_matrix(rotate(matrix))

Выход:

1 5 9 3 
2 6 0 4 
3 7 1 5 
4 8 2 6 

4 3 2 1 
8 7 6 5 
2 1 0 9 
6 5 4 3

Ответ Ника будет работать и для массива NxM с небольшой модификацией (в отличие от NxN).

string[,] orig = new string[n, m];
string[,] rot = new string[m, n];

...

for ( int i=0; i < n; i++ )
  for ( int j=0; j < m; j++ )
    rot[j, n - i - 1] = orig[i, j];

Один из способов подумать об этом состоит в том, что вы переместили центр оси (0,0) из верхнего левого угла в верхний правый угол. Вы просто переходите от одного к другому.

Несколько человек уже привели примеры создания нового массива.

Еще несколько вещей, которые следует учитывать:

(a) Вместо фактического перемещения данных просто обойдите «повернутый» массив другим способом.

(b) Выполнение вращения на месте может быть немного сложнее. Вам понадобится немного места (вероятно, примерно равное по размеру одной строке или столбцу). Есть древняя статья ACM о выполнении транспонирования на месте (http://doi.acm.org/10.1145/355719.355729), но их примерный код представляет собой неприятный FORTRAN, загруженный goto.

Дополнение:

http://doi.acm.org/10.1145/355611.355612 - еще один, предположительно превосходящий алгоритм транспонирования на месте.

Я согласен с этим. Имейте метод, который определяет перевод между исходными данными и "повернутыми" данными.

martinatime 14.09.2008 20:07

Как я сказал в своем предыдущем посте, вот некоторый код на C#, который реализует поворот матрицы O (1) для матрицы любого размера. Для краткости и удобочитаемости здесь нет проверки ошибок или проверки диапазона. Код:

static void Main (string [] args)
{
  int [,]
    //  create an arbitrary matrix
    m = {{0, 1}, {2, 3}, {4, 5}};

  Matrix
    //  create wrappers for the data
    m1 = new Matrix (m),
    m2 = new Matrix (m),
    m3 = new Matrix (m);

  //  rotate the matricies in various ways - all are O(1)
  m1.RotateClockwise90 ();
  m2.Rotate180 ();
  m3.RotateAnitclockwise90 ();

  //  output the result of transforms
  System.Diagnostics.Trace.WriteLine (m1.ToString ());
  System.Diagnostics.Trace.WriteLine (m2.ToString ());
  System.Diagnostics.Trace.WriteLine (m3.ToString ());
}

class Matrix
{
  enum Rotation
  {
    None,
    Clockwise90,
    Clockwise180,
    Clockwise270
  }

  public Matrix (int [,] matrix)
  {
    m_matrix = matrix;
    m_rotation = Rotation.None;
  }

  //  the transformation routines
  public void RotateClockwise90 ()
  {
    m_rotation = (Rotation) (((int) m_rotation + 1) & 3);
  }

  public void Rotate180 ()
  {
    m_rotation = (Rotation) (((int) m_rotation + 2) & 3);
  }

  public void RotateAnitclockwise90 ()
  {
    m_rotation = (Rotation) (((int) m_rotation + 3) & 3);
  }

  //  accessor property to make class look like a two dimensional array
  public int this [int row, int column]
  {
    get
    {
      int
        value = 0;

      switch (m_rotation)
      {
      case Rotation.None:
        value = m_matrix [row, column];
        break;

      case Rotation.Clockwise90:
        value = m_matrix [m_matrix.GetUpperBound (0) - column, row];
        break;

      case Rotation.Clockwise180:
        value = m_matrix [m_matrix.GetUpperBound (0) - row, m_matrix.GetUpperBound (1) - column];
        break;

      case Rotation.Clockwise270:
        value = m_matrix [column, m_matrix.GetUpperBound (1) - row];
        break;
      }

      return value;
    }

    set
    {
      switch (m_rotation)
      {
      case Rotation.None:
        m_matrix [row, column] = value;
        break;

      case Rotation.Clockwise90:
        m_matrix [m_matrix.GetUpperBound (0) - column, row] = value;
        break;

      case Rotation.Clockwise180:
        m_matrix [m_matrix.GetUpperBound (0) - row, m_matrix.GetUpperBound (1) - column] = value;
        break;

      case Rotation.Clockwise270:
        m_matrix [column, m_matrix.GetUpperBound (1) - row] = value;
        break;
      }
    }
  }

  //  creates a string with the matrix values
  public override string ToString ()
  {
    int
      num_rows = 0,
      num_columns = 0;

    switch (m_rotation)
    {
    case Rotation.None:
    case Rotation.Clockwise180:
      num_rows = m_matrix.GetUpperBound (0);
      num_columns = m_matrix.GetUpperBound (1);
      break;

    case Rotation.Clockwise90:
    case Rotation.Clockwise270:
      num_rows = m_matrix.GetUpperBound (1);
      num_columns = m_matrix.GetUpperBound (0);
      break;
    }

    StringBuilder
      output = new StringBuilder ();

    output.Append ("{");

    for (int row = 0 ; row <= num_rows ; ++row)
    {
      if (row != 0)
      {
        output.Append (", ");
      }

      output.Append ("{");

      for (int column = 0 ; column <= num_columns ; ++column)
      {
        if (column != 0)
        {
          output.Append (", ");
        }

        output.Append (this [row, column].ToString ());
      }

      output.Append ("}");
    }

    output.Append ("}");

    return output.ToString ();
  }

  int [,]
    //  the original matrix
    m_matrix;

  Rotation
    //  the current view of the matrix
    m_rotation;
}

Хорошо, я подниму руку, на самом деле он не вносит никаких изменений в исходный массив при вращении. Но в объектно-ориентированной системе это не имеет значения, пока объект выглядит так, как будто он был повернут для клиентов класса. На данный момент класс Matrix использует ссылки на исходные данные массива, поэтому изменение любого значения m1 также изменит m2 и m3. Небольшое изменение в конструкторе для создания нового массива и копирования в него значений поможет разобраться в этом.

Браво! Это очень хорошее решение, и я не знаю, почему это не принятый ответ.

martinatime 14.09.2008 20:11

@martinatime: наверное, потому что он в 5 раз больше

Toad 04.10.2010 21:59

@Toad: написание кода - это всегда компромисс между конкурирующими требованиями: скоростью, размером, стоимостью и т. д.

Skizz 05.10.2010 00:34

правда ... еще одна проблема заключается в том, что матрица на самом деле не поворачивается, а вращается «точно по времени». Это отлично подходит для доступа к нескольким элементам, но было бы ужасно, если бы эта матрица использовалась в вычислениях или манипуляциях с изображениями. Так что говорить O (1) не совсем честно.

Toad 05.10.2010 15:17

@Toad вы тестировали его, чтобы сделать какое-нибудь заявление о том, что это ужасно? Это решение можно оптимизировать, удалив вызовы m_matrix.GetUpperBound() и коммутатор. После этого все сводится к доступу к базовому массиву, что-то вроде этого Matrix[width-x,height-y], который не так уж далек от Matrix[x,y], в моих тестах это была такая же скорость. Еще одним преимуществом является то, что вы можете работать с еще большими вычислениями или изображениями, поскольку вам не нужно сохранять их дважды. Его полностью распараллеливаемый, без перестановки строк / элементов, без копирования памяти. Даже кеширование составляет 2 цикла For с оптимальным значением O (N * N).

FrankM 14.10.2020 18:40

Если вас интересуют всего несколько элементов повернутой матрицы, этот код подойдет. Он читабелен, понятен и просто извлекает элементы. Однако при выполнении полного вращения этот код будет медленным. Для каждого элемента у него есть накладные расходы на вызов метода, поиск в 2-м массиве (который имеет умножение), в каждом наборе / получении есть переключатель, кто знает, что он делает для кэширования памяти и т.д. пух и иметь действительно быструю замену элементов петли было бы намного быстрее, чем это. Было бы более читабельным? Возможно нет.

Toad 15.10.2020 23:57

Вот тот, который выполняет вращение на месте вместо использования совершенно нового массива для хранения результата. Я перестал инициализировать массив и распечатать его. Это работает только для квадратных массивов, но они могут быть любого размера. Накладные расходы на память равны размеру одного элемента массива, поэтому вы можете вращать массив любого размера.

int a[4][4];
int n = 4;
int tmp;
for (int i = 0; i < n / 2; i++)
{
    for (int j = i; j < n - i - 1; j++)
    {
        tmp             = a[i][j];
        a[i][j]         = a[j][n-i-1];
        a[j][n-i-1]     = a[n-i-1][n-j-1];
        a[n-i-1][n-j-1] = a[n-j-1][i];
        a[n-j-1][i]     = tmp;
    }
}

Я вижу как минимум одну ошибку. Если вы собираетесь опубликовать код, протестируйте его или, по крайней мере, скажите, что вы этого не сделали.

Hugh Allen 01.10.2008 11:12

Где? Укажи на это, и я исправлю. Я тестировал его, и он отлично работал как с нечетными, так и с четными массивами.

dagorym 04.10.2008 05:29

Просто взглянув: второй цикл запускает тесты j <-i-1. Похоже, что j всегда> = 0, а -i-1 всегда отрицательно, поэтому тест никогда не проходит.

Eyal 08.05.2009 01:32

Ты прав. Это странно. Я считаю, что там должно быть n, которое я пропустил, когда разместил его. Исправлено в листинге.

dagorym 08.05.2009 18:57

это красивое решение. Разум может совершать такие подвиги, если настроен на это. от O (n2) до O (1)

MoveFast 31.07.2012 22:29

Это не O (1); это все еще O (n ^ 2)

duma 16.11.2012 19:39

Его O (n ^ 2) с памятью O (1).

Neel 04.03.2013 19:50

Я действительно связываю способ деления массива :)

Duke 07.07.2013 12:45

Ключ к применению этого - «на месте»;

Brandon Clark 27.05.2015 04:30

имейте в виду, что это вращение только против часовой стрелки - возможно, вращение по часовой стрелке останется в качестве упражнения для ученика? ;)

Alfredo Gallegos 17.06.2017 01:52

Подобное решение упоминается по ссылке ниже geeksforgeeks.org/inplace-rotate-square-matrix-by-90-degrees

Anand 15.07.2017 08:36

@dagorym: Ой, чувак. Я цеплялся за это как за хорошую головоломку типа «Мне скучно, над чем я могу обдумать». Я придумал свой код транспонирования на месте, но пришел сюда, чтобы найти ваш в значительной степени идентичный моему ... ах, ну. Вот он в Ruby.

require 'pp'
n = 10
a = []
n.times { a << (1..n).to_a }

pp a

0.upto(n/2-1) do |i|
  i.upto(n-i-2) do |j|
    tmp             = a[i][j]
    a[i][j]         = a[n-j-1][i]
    a[n-j-1][i]     = a[n-i-1][n-j-1]
    a[n-i-1][n-j-1] = a[j][n-i-1]
    a[j][n-i-1]     = tmp
  end
end

pp a

Хотя может потребоваться вращение данных на месте (возможно, для обновления физически хранимого представления), становится проще и, возможно, более производительно добавить уровень косвенного обращения к доступу к массиву, возможно, интерфейс:

interface IReadableMatrix
{
    int GetValue(int x, int y);
}

Если ваш Matrix уже реализует этот интерфейс, его можно повернуть с помощью класса декоратор следующим образом:

class RotatedMatrix : IReadableMatrix
{
    private readonly IReadableMatrix _baseMatrix;

    public RotatedMatrix(IReadableMatrix baseMatrix)
    {
        _baseMatrix = baseMatrix;
    }

    int GetValue(int x, int y)
    {
        // transpose x and y dimensions
        return _baseMatrix(y, x);
    }
}

Таким же образом можно добиться поворота на + 90 / -90 / 180 градусов, горизонтального / вертикального отражения и масштабирования.

Производительность необходимо будет измерить в вашем конкретном сценарии. Однако операция O (n ^ 2) теперь заменена вызовом O (1). Это вызов виртуального метода, который является медленнее, чем прямой доступ к массиву, поэтому это зависит от того, как часто повернутый массив используется после вращения. Если его использовать один раз, то такой подход однозначно выиграет. Если его чередовать, а затем использовать в долгосрочной системе в течение нескольких дней, ротация на месте может работать лучше. Это также зависит от того, сможете ли вы принять предоплату.

Как и в случае со всеми проблемами производительности, измеряйте, измеряйте, измеряйте!

+1 ... И если матрица действительно большая, и вы получаете доступ только к паре элементов (редкое использование), это еще более эффективно

lothar 04.06.2009 06:25

Что здесь происходит под одеялом? Это просто псевдоним или память действительно меняется?

i_am_jorf 03.11.2009 17:51

@jeffamaphone, память не меняется. Думаю, вы могли бы подумать об этом как о псевдониме, да.

Drew Noakes 04.11.2009 00:35

Будет ли это работать, если я вызову GetValue (3,3). Он всегда будет возвращать исходное значение, и транспонирование не происходит?

Sesh 19.11.2009 19:30

@Sesh, да, он все еще работает, потому что (A, B) совпадает с (B, A), когда A == B.

Drew Noakes 25.11.2009 13:34

Кажется немного несправедливым называть это временным решением за O (1). Чтобы решить проблему, поставленную OP, это все равно займет время O (n ^ 2). Более того, это не решит проблему, потому что возвращает транспонировать. В данном примере транспонирование не используется в качестве решения.

Vlad the Impala 20.04.2010 08:32

@Goose Bumper - дело в том, что транспонирование не обязательно должно происходить физически. Распечатка матрицы занимает O (n ^ 2), но также и любая операция исчерпывающего чтения, так что от этого никуда не деться. Транспонирование было O (1), и каждое отдельное чтение также O (1), поэтому я придерживаюсь своего утверждения, что это решение имеет постоянное время для вращения и последующих чтений. Как я уже упоминал в ответе, этот метод не всегда самый подходящий.

Drew Noakes 20.04.2010 22:43

@ Пол Беттс, не могли бы уточнить? Я не пытаюсь не согласиться, но мне просто интересно узнать о ваших рассуждениях.

Akusete 29.06.2010 07:36

@ Пол Беттс, пожалуйста, объясните подробнее. Я не понимаю, почему это не O (1). В любом случае, кто сказал, что я хотел работать на вас? :)

Drew Noakes 29.06.2010 08:36

Поскольку для получения полного содержимого нового массива я должен написать функцию: for (i = 1 to n) {for (j = 1 to n) {rotated.GetValue (i, j); }} Вы не волшебным образом сделали это время постоянным, вы просто заставили кого-то написать цикл.

Ana Betts 30.06.2010 03:29

Теперь, если все, что вам нужно, это первые 3 элементы матрицы, это прекрасное решение, но проблема состоит в том, чтобы получить полностью преобразованную матрицу (т.е. предполагая, что вам нужны элементы матрицы все). Вызов этого O (1) - это метод кредитного обмена по умолчанию для анализа алгоритмов - вы не решили проблему, вы просто передали ее кому-то другому :)

Ana Betts 30.06.2010 03:31

@ Пол Беттс: Я понимаю вашу точку зрения, но, как я писал выше в комментариях, даже если у вас действительно есть транспонированная матрица, вам все равно придется написать цикл, если вы хотите прочитать значения. Таким образом, чтение всех значений из матрицы всегда O (N ^ 2) независимо. Разница здесь в том, что если вы снова транспонируете, вращаете, масштабируете, масштабируете и т. д., Вы все равно принимаете удар O (N ^ 2) только один раз. Как я уже сказал, это не всегда лучшее решение, но во многих случаях оно уместно и того стоит. OP, похоже, искал волшебное решение, и оно максимально близко к вам.

Drew Noakes 30.06.2010 08:29

Как вы говорите, если вам нужно всего 3 элемента, это быстрее, но я утверждаю, что в большинстве случаев это быстрее в любом случае. Что хорошего в байтах, размещенных в памяти, если вы их не читаете? Независимо от того, будет ли это произвольный или последовательный доступ, их чтение все равно придется заплатить. Это решение просто избавляет вас от необходимости читать их все во время передачи, а затем опять таки позже. Вы читаете только один раз. Если вы забиваете матричный массив для чтения, вам может потребоваться постоянная версия, потому что стоимость отправки виртуальных вызовов может начать перевешивать преимущества этого подхода.

Drew Noakes 30.06.2010 08:48

Еще одно соображение заключается в том, является ли исходная матрица неизменной. Этот метод работает только в том случае, если источник не меняется под вами. Есть другой способ думать об этом подходе. Вместо того, чтобы возвращать массив, мы возвращаем обещание предоставить массив, когда (и если) он понадобится. Он похож на IEnumerable. «Расчет» - ленивый.

Drew Noakes 30.06.2010 08:54

Мне нравится этот ответ, но я хочу кое-что указать. Распечатка декорированной матрицы (и выполнение других последовательных операций чтения в целом) может быть намного медленнее, чем то же самое с матрицей, которая была повернута в памяти, и это не только из-за вызовов виртуальных методов. Для большой матрицы вы значительно увеличите количество промахов в кэше, читая «вниз», а не «поперек».

Mike Daniels 04.10.2010 22:23

@drew noakes, @paul betts: как бы вы это ни объяснили, точка зрения Пола Бетта верна. Вы не поворачивали матрицу, но добавляли косвенные слои, которые будут выполнять преобразование «как раз вовремя». Выполнение чего-либо существенного с этой матрицей (вычисления матриц или манипуляции с изображениями) из-за этого серьезно замедлится.

Toad 05.10.2010 15:22

@ Жаба, ага. Как я уже сказал в посте и в комментариях, все зависит от обстоятельств. Здесь никто не показал это решение, и стоит подумать, даже если вы им не пользуетесь. В некоторых случаях это будет намного лучше, чем вращение массива в памяти.

Drew Noakes 05.10.2010 15:59

@MikeDaniels: Это не совсем полная история, потому что, если бы вы просто повернули массив на место "сложным путем" в начале, вам бы тогда пришлось принимать те же самые обращения к кешу. Решение Дрю будет намного медленнее, только если вы выполните операции много с элементами в строковом порядке после их «поворота» (декорирования).

j_random_hacker 19.03.2013 18:13

Python:

rotated = list(zip(*original[::-1]))

и против часовой стрелки:

rotated_ccw = list(zip(*original))[::-1]

Как это работает:

zip(*original) меняет местами оси двумерных массивов, складывая соответствующие элементы из списков в новые списки. (Оператор * сообщает функции распределить содержащиеся списки в аргументы)

>>> list(zip(*[[1,2,3],[4,5,6],[7,8,9]]))
[[1,4,7],[2,5,8],[3,6,9]]

Оператор [::-1] меняет местами элементы массива (см. Расширенные фрагменты или этот вопрос):

>>> [[1,2,3],[4,5,6],[7,8,9]][::-1]
[[7,8,9],[4,5,6],[1,2,3]]

Наконец, их объединение приведет к преобразованию вращения.

При изменении размещения [::-1] списки на разных уровнях матрицы меняются местами.

Я считаю, что этот код исходит от Питера Норвига: norvig.com/python-iaq.html

Josip 07.07.2009 18:43

Вы можете использовать zip(*reversed(original)) вместо zip(*original[::-1]), чтобы не создавать лишнюю копию исходного списка.

Boris 28.12.2019 10:46

#include <iostream>
#include <iomanip>

using namespace std;
const int SIZE=3;
void print(int a[][SIZE],int);
void rotate(int a[][SIZE],int);

void main()
{
    int a[SIZE][SIZE] = {{11,22,33},{44,55,66},{77,88,99}};
    cout<<"the array befor rotate\n";

    print(a,SIZE);
    rotate( a,SIZE);
    cout<<"the array after rotate\n";
    print(a,SIZE);
    cout<<endl;

}

void print(int a[][SIZE],int SIZE)
{
    int i,j;
    for(i=0;i<SIZE;i++)
       for(j=0;j<SIZE;j++)
          cout<<a[i][j]<<setw(4);
}

void rotate(int a[][SIZE],int SIZE)
{
    int temp[3][3],i,j;
    for(i=0;i<SIZE;i++)
       for(j=0;j<SIZE/2.5;j++)
       {
           temp[i][j]= a[i][j];
           a[i][j]= a[j][SIZE-i-1] ;
           a[j][SIZE-i-1] =temp[i][j];

       }
}

short normal[4][4] = {{8,4,7,5},{3,4,5,7},{9,5,5,6},{3,3,3,3}};

short rotated[4][4];

for (int r = 0; r < 4; ++r)
{
  for (int c = 0; c < 4; ++c)
  {
    rotated[r][c] = normal[c][3-r];
  }
}

Простой метод C++, хотя в большом массиве будут большие накладные расходы на память.

Среди всех этих ответов я нашел и протестировал тот, который компактен и достаточно вращается.

dlewin 07.04.2017 16:37

Это лучшая версия на Java: я сделал ее для матрицы с другой шириной и высотой.

  • h здесь высота матрицы после поворота
  • w здесь ширина матрицы после поворота

 

public int[][] rotateMatrixRight(int[][] matrix)
{
    /* W and H are already swapped */
    int w = matrix.length;
    int h = matrix[0].length;
    int[][] ret = new int[h][w];
    for (int i = 0; i < h; ++i) {
        for (int j = 0; j < w; ++j) {
            ret[i][j] = matrix[w - j - 1][i];
        }
    }
    return ret;
}


public int[][] rotateMatrixLeft(int[][] matrix)
{
    /* W and H are already swapped */
    int w = matrix.length;
    int h = matrix[0].length;   
    int[][] ret = new int[h][w];
    for (int i = 0; i < h; ++i) {
        for (int j = 0; j < w; ++j) {
            ret[i][j] = matrix[j][h - i - 1];
        }
    }
    return ret;
}

Этот код основан на сообщении Ника Берарди.

Спасибо. Здесь был самый ясный код Java. Вопрос - Как вы / Ник придумали часть [w - j - 1]? Глядя на ответ @tweaking, я вижу, как вы могли бы получить это с помощью примеров индукции / решения. Просто интересно, так ли это было получено или это основано на каком-то математическом принципе, относящемся к Матрицам.

Quest Monger 30.08.2015 06:37

PHP:

<?php    
$a = array(array(1,2,3,4),array(5,6,7,8),array(9,0,1,2),array(3,4,5,6));
$b = array(); //result

while(count($a)>0)
{
    $b[count($a[0])-1][] = array_shift($a[0]);
    if (count($a[0])==0)
    {
         array_shift($a);
    }
}

Начиная с PHP5.6, транспонирование массива может выполняться с помощью специального вызова array_map(). Другими словами, столбцы преобразуются в строки.

Код: (Демо)

$array = [
    [1, 2, 3, 4],
    [5, 6, 7, 8],
    [9, 0, 1, 2],
    [3, 4, 5, 6]
];
$transposed = array_map(null, ...$array);

$ транспонировано:

[
    [1, 5, 9, 3],
    [2, 6, 0, 4],
    [3, 7, 1, 5],
    [4, 8, 2, 6]
]

Все текущие решения имеют накладные расходы O (n ^ 2) в качестве рабочего места (это исключает этих грязных читеров ООП!). Вот решение с использованием памяти O (1), поворот матрицы на 90 градусов вправо. Расширяемость винта, эта присоска работает быстро!

#include <algorithm>
#include <cstddef>

// Rotates an NxN matrix of type T 90 degrees to the right.
template <typename T, size_t N>
void rotate_matrix(T (&matrix)[N][N])
{
    for(size_t i = 0; i < N; ++i)
        for(size_t j = 0; j <= (N-i); ++j)
            std::swap(matrix[i][j], matrix[j][i]);
}

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я на самом деле не тестировал это. Давай поиграем в жучок!

Разве это не просто транспонирование матрицы, а не ее поворот?

recursive 05.10.2011 17:39

Рубиновый путь: .transpose.map &:reverse

Это еще проще: array.reverse.transpose вращает массив по часовой стрелке, а array.transpose.reverse вращает его против часовой стрелки. map не нужен.

Giorgi Gzirishvili 11.07.2019 14:48

Время O (n ^ 2) и алгоритм пространства O (1) (без каких-либо обходных путей и непонятных вещей!)

Повернуть на +90:

  1. Транспонировать
  2. Переверните каждую строку

Повернуть на -90:

Способ 1:

  1. Транспонировать
  2. Переверните каждый столбец

Способ 2:

  1. Переверните каждую строку
  2. Транспонировать

Повернуть на +180:

Способ 1: дважды повернуть на +90

Способ 2: перевернуть каждую строку, а затем перевернуть каждый столбец (транспонировать)

Повернуть на -180:

Способ 1: повернуть на -90 дважды

Способ 2: перевернуть каждый столбец, а затем перевернуть каждую строку

Способ 3: повернуть на +180, так как они такие же

Это было очень полезно для меня; Я смог написать алгоритм, когда узнал «версию [псевдокода» этой операции. Спасибо!

duma 16.11.2012 19:49

Один из моих любимых SO-ответов всех времен. Очень поучительно!

g33kz0r 04.04.2013 06:49

Вот реализация JavaScript JSFiddle, если кому-то интересно.

Mr. Polywhirl 25.04.2014 14:43

Повернуть на -90: (1) Поменять местами каждую строку; (2) Транспонировать. Haskell: rotateCW = map reverse . transpose и rotateCCW = transpose . map reverse

Thomas Eding 08.09.2014 21:01

Чтобы повернуть на -90, реверсирование каждого ряд и затем транспонирование будет быстрее, чем транспонирование с последующим обращением каждого столбец. Согласованность кеша - большое дело

Marco M. 18.09.2014 19:35

Пространство не было бы O (1) для простого решения «Транспонирование матрицы на месте» не так просто. Для простого решения вы должны использовать временную матрицу для хранения транспонированной матрицы. Тем не менее, хорошее решение!

puneet 15.12.2015 23:15

Красивый. Следует отметить, что пространство O (1) невозможно, если размеры разные, независимо от того, какой другой массив должен быть создан.

SMKS 03.04.2017 08:32

В чем разница между поворотом на 180 и -180?

Qian Chen 21.05.2017 11:47

@ElgsQianChen Нет. 90, 180, 270 соответствуют -270, -180, -90 в другом направлении соответственно. Следовательно, поворот на 90 совпадает с поворотом на -270 и так далее. Фактически, для всех случаев требуется только три уникальных поворота (если 0, можно просто вернуть исходную матрицу).

James M 02.04.2018 23:41

Блестящее решение. Это имеет некоторое сходство с вращением одномерного массива - что, IMHO, является его яркостью. В Интернете есть и другие решения, и это лучшее.

Arun 08.08.2018 21:56

Я единственный, кто не может доказать правильность этого алгоритма? :(

Ajay Gaur 03.12.2018 23:14

For i:= 0 to X do For j := 0 to X do graphic[j][i] := graphic2[X-i][j]

X - это размер массива, в котором находится графика.

вот метод поворота в пространстве, java, только для квадрата. для неквадратного 2d-массива вам все равно придется создать новый массив.

private void rotateInSpace(int[][] arr) {
    int z = arr.length;
    for (int i = 0; i < z / 2; i++) {
        for (int j = 0; j < (z / 2 + z % 2); j++) {
            int x = i, y = j;
            int temp = arr[x][y];
            for (int k = 0; k < 4; k++) {
                int temptemp = arr[y][z - x - 1];
                arr[y][z - x - 1] = temp;
                temp = temptemp;

                int tempX = y;
                y = z - x - 1;
                x = tempX;
            }
        }
    }
}

код для поворота массива 2d любого размера путем создания нового массива:

private int[][] rotate(int[][] arr) {
    int width = arr[0].length;
    int depth = arr.length;
    int[][] re = new int[width][depth];
    for (int i = 0; i < depth; i++) {
        for (int j = 0; j < width; j++) {
            re[j][depth - i - 1] = arr[i][j];
        }
    }
    return re;
}

Это моя реализация, сложность памяти C, O (1), вращение на месте на 90 градусов по часовой стрелке:

#include <stdio.h>

#define M_SIZE 5

static void initMatrix();
static void printMatrix();
static void rotateMatrix();

static int m[M_SIZE][M_SIZE];

int main(void){
    initMatrix();
    printMatrix();
    rotateMatrix();
    printMatrix();

    return 0;
}

static void initMatrix(){
    int i, j;

    for(i = 0; i < M_SIZE; i++){
        for(j = 0; j < M_SIZE; j++){
            m[i][j] = M_SIZE*i + j + 1;
        }
    }
}

static void printMatrix(){
    int i, j;

    printf("Matrix\n");
    for(i = 0; i < M_SIZE; i++){
        for(j = 0; j < M_SIZE; j++){
            printf("%02d ", m[i][j]);
        }
        printf("\n");
    }
    printf("\n");
}

static void rotateMatrix(){
    int r, c;

    for(r = 0; r < M_SIZE/2; r++){
        for(c = r; c < M_SIZE - r - 1; c++){
            int tmp = m[r][c];

            m[r][c] = m[M_SIZE - c - 1][r];
            m[M_SIZE - c - 1][r] = m[M_SIZE - r - 1][M_SIZE - c - 1];
            m[M_SIZE - r - 1][M_SIZE - c - 1] = m[c][M_SIZE - r - 1];
            m[c][M_SIZE - r - 1] = tmp;
        }
    }
}

#transpose - это стандартный метод класса Array Ruby, поэтому:

% irb
irb(main):001:0> m = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 0, 1, 2], [3, 4, 5, 6]]
=> [[1, 2, 3, 4], [5, 6, 7, 8], [9, 0, 1, 2], [3, 4, 5, 6]] 
irb(main):002:0> m.reverse.transpose
=> [[3, 9, 5, 1], [4, 0, 6, 2], [5, 1, 7, 3], [6, 2, 8, 4]]

Реализация представляет собой функцию транспонирования n ^ 2, написанную на C. Вы можете увидеть это здесь: http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-transpose выбрав «щелкните, чтобы переключить источник» рядом с «транспонировать».

Я помню лучше, чем решения O (n ^ 2), но только для специально построенных матриц (например, разреженных матриц)

Код C для поворота матрицы на 90 градусов по часовой стрелке НА МЕСТЕ для любой матрицы M * N

void rotateInPlace(int * arr[size][size], int row, int column){
    int i, j;
    int temp = row>column?row:column;
    int flipTill = row < column ? row : column;
    for(i=0;i<flipTill;i++){
        for(j=0;j<i;j++){
            swapArrayElements(arr, i, j);
        }
    }

    temp = j+1;

    for(i = row>column?i:0; i<row; i++){
            for(j=row<column?temp:0; j<column; j++){
                swapArrayElements(arr, i, j);
            }
    }

    for(i=0;i<column;i++){
        for(j=0;j<row/2;j++){
            temp = arr[i][j];
            arr[i][j] = arr[i][row-j-1];
            arr[i][row-j-1] = temp;
        }
    }
}

Вот моя попытка поворота матрицы на 90 градусов, которая представляет собой двухэтапное решение в C. Сначала переставьте матрицу на место, а затем поменяйте местами столбцы.

#define ROWS        5
#define COLS        5

void print_matrix_b(int B[][COLS], int rows, int cols) 
{
    for (int i = 0; i <= rows; i++) {
        for (int j = 0; j <=cols; j++) {
            printf("%d ", B[i][j]);
        }
        printf("\n");
    }
}

void swap_columns(int B[][COLS], int l, int r, int rows)
{
    int tmp;
    for (int i = 0; i <= rows; i++) {
        tmp = B[i][l];
        B[i][l] = B[i][r];
        B[i][r] = tmp;
    }
}


void matrix_2d_rotation(int B[][COLS], int rows, int cols)
{
    int tmp;
    // Transpose the matrix first
    for (int i = 0; i <= rows; i++) {
        for (int j = i; j <=cols; j++) {
            tmp = B[i][j];
            B[i][j] = B[j][i];
            B[j][i] = tmp;
        }
    }
    // Swap the first and last col and continue until
    // the middle.
    for (int i = 0; i < (cols / 2); i++)
        swap_columns(B, i, cols - i, rows);
}



int _tmain(int argc, _TCHAR* argv[])
{
    int B[ROWS][COLS] = { 
                  {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}
                        };

    matrix_2d_rotation(B, ROWS - 1, COLS - 1);

    print_matrix_b(B, ROWS - 1, COLS -1);
    return 0;
}

Алгоритм памяти O (1):

  1. поверните самые внешние данные, тогда вы можете получить следующий результат:

    [3][9][5][1]
    [4][6][7][2]
    [5][0][1][3]
    [6][2][8][4]
    

Чтобы сделать это вращение, мы знаем

    dest[j][n-1-i] = src[i][j]

Обратите внимание на следующее: а (0,0) -> а (0,3) а (0,3) -> а (3,3) а (3,3) -> а (3,0) а (3,0) -> а (0,0)

Следовательно, это круг, вы можете вращать N элементов за один цикл. Сделайте этот цикл N-1, затем вы можете вращать самые внешние элементы.

  1. Теперь вы можете внутренний тот же вопрос для 2X2.

Поэтому мы можем заключить это следующим образом:

function rotate(array, N)
{
    Rotate outer-most data
    rotate a new array with N-2 or you can do the similar action following step1
}

Вот версия Java:

public static void rightRotate(int[][] matrix, int n) {
    for (int layer = 0; layer < n / 2; layer++) {
        int first = layer;
        int last = n - 1 - first;
        for (int i = first; i < last; i++) {
           int offset = i - first;
           int temp = matrix[first][i];
           matrix[first][i] = matrix[last-offset][first];
           matrix[last-offset][first] = matrix[last][last-offset];
           matrix[last][last-offset] = matrix[i][last];
           matrix[i][last] = temp;
        }
    }
}

метод сначала вращает самый внешний слой, а затем последовательно перемещается к внутреннему слою.

private static int[][] rotate(int[][] matrix, int n) {
    int[][] rotated = new int[n][n];
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            rotated[i][j] = matrix[n-j-1][i];
        }
    }
    return rotated;
}

На каком это языке?

Don Cruickshank 02.08.2013 13:44

вот моя реализация на месте в C

void rotateRight(int matrix[][SIZE], int length) {

    int layer = 0;

    for (int layer = 0; layer < length / 2; ++layer) {

        int first = layer;
        int last = length - 1 - layer;

        for (int i = first; i < last; ++i) {

            int topline = matrix[first][i];
            int rightcol = matrix[i][last];
            int bottomline = matrix[last][length - layer - 1 - i];
            int leftcol = matrix[length - layer - 1 - i][first];

            matrix[first][i] = leftcol;
            matrix[i][last] = topline;
            matrix[last][length - layer - 1 - i] = rightcol;
            matrix[length - layer - 1 - i][first] = bottomline;
        }
    }
}

Реализация псевдокода +90 ямки (например, транспонировать, а затем перевернуть каждую строку) в JavaScript:

function rotate90(a){
  // transpose from http://www.codesuck.com/2012/02/transpose-javascript-array-in-one-line.html
  a = Object.keys(a[0]).map(function (c) { return a.map(function (r) { return r[c]; }); });
  // row reverse
  for (i in a){
    a[i] = a[i].reverse();
  }
  return a;
}

Здесь много хорошего кода, но я просто хочу показать, что происходит геометрически, чтобы вы могли немного лучше понять логику кода. Вот как я подхожу к этому.

во-первых, не путайте это с транспонированием, это очень просто.

основная идея состоит в том, чтобы рассматривать его как слои и вращать по одному слою за раз.

скажем, у нас есть 4х4

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

после того, как мы повернем его по часовой стрелке на 90, мы получим

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

так что давайте разложим это, сначала мы по существу повернем 4 угла

1           4


13          16

Затем мы вращаем следующий ромб, который как бы наклонен

    2
            8
9       
        15

а затем второй перекошенный ромб

        3
5           
            12
    14

так что это заботится о внешнем крае, так что мы, по сути, делаем эту оболочку за раз, пока

наконец, средний квадрат (или, если он нечетный, только последний элемент, который не двигается)

6   7
10  11

Итак, теперь давайте выясним индексы каждого слоя, предположим, что мы всегда работаем с самым внешним слоем, мы делаем

[0,0] -> [0,n-1], [0,n-1] -> [n-1,n-1], [n-1,n-1] -> [n-1,0], and [n-1,0] -> [0,0]
[0,1] -> [1,n-1], [1,n-2] -> [n-1,n-2], [n-1,n-2] -> [n-2,0], and [n-2,0] -> [0,1]
[0,2] -> [2,n-2], [2,n-2] -> [n-1,n-3], [n-1,n-3] -> [n-3,0], and [n-3,0] -> [0,2]

так далее и так далее пока мы не пройдем половину края

так что в целом картина

[0,i] -> [i,n-i], [i,n-i] -> [n-1,n-(i+1)], [n-1,n-(i+1)] -> [n-(i+1),0], and [n-(i+1),0] to [0,i]

что значит «на полпути к краю»? Я вижу, как много алгоритмов зацикливаются до N / 2, а другие зацикливаются до N, но я не вижу, откуда исходит N / 2.

PDN 07.03.2016 06:07

Я считаю, что это то же самое решение, что и при взломе интервью по кодированию. Но мне нравится пошаговое объяснение. Очень красиво и основательно.

Naphstor 08.11.2016 21:10

@PDN Этот ответ подробно объясняет это.

Mathias Bynens 05.02.2017 12:57

Невозможно сделать это быстрее, чем O (n ^ 2) для вращения на месте, по той причине, что если мы хотим повернуть матрицу, мы должны коснуться всего элемента n ^ 2 хотя бы один раз, независимо от того, какой алгоритм вы реализуете.

В этом случае п должно относиться к количеству элементов в матрице, а не к длине одной из ее сторон, что делает поворот матрицы O (n) (т.е. линейным)

Boris 28.12.2019 11:07

Время - O (N), Пространство - O (1)

public void rotate(int[][] matrix) {
    int n = matrix.length;
    for (int i = 0; i < n / 2; i++) {
        int last = n - 1 - i;
        for (int j = i; j < last; j++) {
            int top = matrix[i][j];
            matrix[i][j] = matrix[last - j][i];
            matrix[last - j][i] = matrix[last][last - j];
            matrix[last][last - j] = matrix[j][last];
            matrix[j][last] = top;
        }
    }
}

Это не O (1). Это O (n).

Jason Oster 15.10.2014 07:52

@JasonOster Я считаю, что это пространство O (1), поскольку оно не требует дополнительного места.

ffledgling 27.10.2014 14:55

@ffledgling Моя ошибка. O (1) космическая сложность, да. O (n) временная сложность.

Jason Oster 08.11.2014 03:49

Космическая сложность тоже O (n). Сложность пространства должна включать пространство размера входной переменной. careercup.com/question?id=14952322

Jason Heo 01.01.2016 18:59

Как я могу изменить это, чтобы оно работало против часовой стрелки?

MD XF 21.01.2018 06:57

Вот рекурсивный способ PHP:

$m = array();
            $m[0] = array('a', 'b', 'c');
            $m[1] = array('d', 'e', 'f');
            $m[2] = array('g', 'h', 'i');
            $newMatrix = array();

            function rotateMatrix($m, $i = 0, &$newMatrix)
            {
                foreach ($m as $chunk) {
                    $newChunk[] = $chunk[$i];
                }
                $newMatrix[] = array_reverse($newChunk);
                $i++;

                if ($i < count($m)) {
                    rotateMatrix($m, $i, $newMatrix);
                }
            }

            rotateMatrix($m, 0, $newMatrix);
            echo '<pre>';
            var_dump($newMatrix);
            echo '<pre>';

Моя версия вращения:

void rotate_matrix(int *matrix, int size)
{

int result[size*size];

    for (int i = 0; i < size; ++i)
        for (int j = 0; j < size; ++j)
            result[(size - 1 - i) + j*size] = matrix[i*size+j];

    for (int i = 0; i < size*size; ++i)
        matrix[i] = result[i];
}

В нем мы меняем последний столбец на первую строку и так далее. Это может быть неоптимально, но понятно для понимания.

Для начинающих программистов, на простом C++. (Материал Borland)

#include<iostream.h>
#include<conio.h>

int main()
{
    clrscr();

    int arr[10][10];        // 2d array that holds input elements 
    int result[10][10];     //holds result

    int m,n;                //rows and columns of arr[][]
    int x,y;                //rows and columns of result[][]

    int i,j;                //loop variables
    int t;                  //temporary , holds data while conversion

    cout<<"Enter no. of rows and columns of array: ";
    cin>>m>>n;
    cout<<"\nEnter elements of array: \n\n";
    for(i = 0; i < m; i++)
    {
        for(j = 0; j<n ; j++)
        {
          cin>>arr[i][j];         // input array elements from user
        }
    }


   //rotating matrix by +90 degrees

    x = n ;                      //for non-square matrix
    y = m ;     

    for(i = 0; i < x; i++)
    {  t = m-1;                     // to create required array bounds
       for(j = 0; j < y; j++)
       {
          result[i][j] = arr[t][i];
          t--;
       }
   }

   //print result

   cout<<"\nRotated matrix is: \n\n";
   for(i = 0; i < x; i++)
   {
       for(j = 0; j < y; j++)
       {
             cout<<result[i][j]<<" ";
       }
       cout<<"\n";
   }

   getch();
   return 0;
}

#!/usr/bin/env python

original = [ [1,2,3],
             [4,5,6],
             [7,8,9] ]

# Rotate matrix 90 degrees...
for i in map(None,*original[::-1]):
    print str(i) + '\n'

Это приводит к повороту сторон на 90 градусов (т.е. 123 (верхняя сторона) теперь 741 (левая сторона).

Это решение Python работает, потому что оно использует нарезку с отрицательным шагом для изменения порядка строк (вывод 7 наверх).

original = [ [7,8,9],
             [4,5,6],
             [1,2,3] ]

Затем он использует map (вместе с подразумеваемой функцией идентичности, которая является результатом map с None в качестве первого аргумента) вместе с * для распаковки всех элементов в последовательности, чтобы перегруппировать столбцы (т.е. первые элементы помещаются в кортеж вместе, 2-е элементы объединяются в кортеж и т. д.). Фактически, вы получаете отдачу при следующей перегруппировке:

original = [[7,8,9],
             [4,5,6],
             [1,2,3]]

map(None, ... не поддерживается в Python 3, и как в Py 2, так и в Py 3, лучшим подходом было бы заменить map(None, *original[::-1]) на zip(*original[::-1]), который выполняет то же самое, когда все строки имеют одинаковую длину, без попытки обработать функцию преобразования.

ShadowRanger 18.12.2015 04:36

PHP:

array_unshift($array, null);
$array = call_user_func_array("array_map", $array);

Если вам нужно повернуть прямоугольный двухмерный массив на 90 градусов, добавьте следующую строку до или после (в зависимости от направления вращения, которое вам нужно) приведенного выше кода:

$array = array_reverse($array);

С линейной точки зрения рассмотрим матрицы:

    1 2 3        0 0 1
A = 4 5 6    B = 0 1 0
    7 8 9        1 0 0

Теперь возьмите транспонирование

     1 4 7
A' = 2 5 8
     3 6 9

И рассмотрим действие A 'на B или B на A'.
Соответственно:

      7 4 1          3 6 9
A'B = 8 5 2    BA' = 2 5 8
      9 6 3          1 4 7

Это можно расширить для любой матрицы размера n x n. И быстро применяем эту концепцию в коде:

void swapInSpace(int** mat, int r1, int c1, int r2, int c2)
{
    mat[r1][c1] ^= mat[r2][c2];
    mat[r2][c2] ^= mat[r1][c1];
    mat[r1][c1] ^= mat[r2][c2];
}

void transpose(int** mat, int size)
{
    for (int i = 0; i < size; i++)
    {
        for (int j = (i + 1); j < size; j++)
        {
            swapInSpace(mat, i, j, j, i);
        }
    }
}

void rotate(int** mat, int size)
{
    //Get transpose
    transpose(mat, size);

    //Swap columns
    for (int i = 0; i < size / 2; i++)
    {
        for (int j = 0; j < size; j++)
        {
            swapInSpace(mat, i, j, size - (i + 1), j);
        }
    }
}

Уже есть много ответов, и я нашел два, претендующих на временную сложность O (1). Алгоритм настоящий O (1) заключается в том, чтобы оставить нетронутым хранилище массива и изменить способ индексации его элементов. Здесь цель состоит в том, чтобы не потреблять дополнительную память и не требовать дополнительного времени для итерации данных.

Повороты на 90, -90 и 180 градусов - это простые преобразования, которые можно выполнять, если вы знаете, сколько строк и столбцов находится в вашем 2D-массиве; Чтобы повернуть любой вектор на 90 градусов, поменяйте оси местами и инвертируйте ось Y. Для -90 градусов поменяйте местами оси и инвертируйте ось X. Для 180 градусов инвертируйте обе оси без замены местами.

Возможны дальнейшие преобразования, такие как зеркальное отображение по горизонтали и / или вертикали путем независимого отрицания осей.

Это можно сделать, например, метод доступа. Приведенные ниже примеры являются функциями JavaScript, но эти концепции в равной степени применимы ко всем языкам.

 // Get an array element in column/row order
 var getArray2d = function(a, x, y) {
   return a[y][x];
 };

 //demo
 var arr = [
   [5, 4, 6],
   [1, 7, 9],
   [-2, 11, 0],
   [8, 21, -3],
   [3, -1, 2]
 ];

 var newarr = [];
 arr[0].forEach(() => newarr.push(new Array(arr.length)));

 for (var i = 0; i < newarr.length; i++) {
   for (var j = 0; j < newarr[0].length; j++) {
     newarr[i][j] = getArray2d(arr, i, j);
   }
 }
 console.info(newarr);

// Get an array element rotated 90 degrees clockwise
function getArray2dCW(a, x, y) {
  var t = x;
  x = y;
  y = a.length - t - 1;
  return a[y][x];
}

//demo
var arr = [
  [5, 4, 6],
  [1, 7, 9],
  [-2, 11, 0],
  [8, 21, -3],
  [3, -1, 2]
];

var newarr = [];
arr[0].forEach(() => newarr.push(new Array(arr.length)));

for (var i = 0; i < newarr[0].length; i++) {
  for (var j = 0; j < newarr.length; j++) {
    newarr[j][i] = getArray2dCW(arr, i, j);
  }
}
console.info(newarr);

// Get an array element rotated 90 degrees counter-clockwise
function getArray2dCCW(a, x, y) {
  var t = x;
  x = a[0].length - y - 1;
  y = t;
  return a[y][x];
}

//demo
var arr = [
  [5, 4, 6],
  [1, 7, 9],
  [-2, 11, 0],
  [8, 21, -3],
  [3, -1, 2]
];

var newarr = [];
arr[0].forEach(() => newarr.push(new Array(arr.length)));

for (var i = 0; i < newarr[0].length; i++) {
  for (var j = 0; j < newarr.length; j++) {
    newarr[j][i] = getArray2dCCW(arr, i, j);
  }
}
console.info(newarr);

// Get an array element rotated 180 degrees
function getArray2d180(a, x, y) {
  x = a[0].length - x - 1;
  y = a.length - y - 1;
  return a[y][x];
}

//demo
var arr = [
  [5, 4, 6],
  [1, 7, 9],
  [-2, 11, 0],
  [8, 21, -3],
  [3, -1, 2]
];

var newarr = [];
arr.forEach(() => newarr.push(new Array(arr[0].length)));

for (var i = 0; i < newarr[0].length; i++) {
  for (var j = 0; j < newarr.length; j++) {
    newarr[j][i] = getArray2d180(arr, i, j);
  }
}
console.info(newarr);

Этот код предполагает наличие массива вложенных массивов, где каждый внутренний массив представляет собой строку.

Метод позволяет вам читать (или записывать) элементы (даже в случайном порядке), как если бы массив был повернут или преобразован. Теперь просто выберите нужную функцию для вызова, возможно, по ссылке, и вперед!

Концепция может быть расширена для применения преобразований аддитивно (и неразрушающим образом) с помощью методов доступа. Включая произвольные угловые повороты и масштабирование.

Однако на самом деле ни один из них не вращался из исходного массива. Первый, конечный результат просто транспонируется. Во втором случае вы, кажется, только что перемешали строки или отразили их по центру по горизонтали. Третий, вы только перевернули строки, а четвертый также транспонирован. Ни один из них фактически не был «повернут».

SM177Y 28.12.2018 04:16

В последних двух примерах есть некоторые ошибки. Исправить несложно. Я прямо указал, что это решение нет - вращение на месте. Это функция преобразования, что делает ее пригодной для ленивых итераций.

Jason Oster 28.01.2019 22:50

За исключением того, что ротации нет, поэтому вы фактически не ответили на то, что спросил OP.

SM177Y 26.02.2019 03:42

@ SM177Y Другой редактор добавил к моему ответу нерабочий пример кода. Я вижу, как это вас смутило. Исправлены ошибки в итерационных циклах. Предоставленные функции фактически "вращают" данные в массивах.

Jason Oster 27.02.2019 08:29

Также важной деталью является то, что пример кода действительно размывает исходный ответ, который я предоставил, который пытался проиллюстрировать мощь функциональных преобразований над решениями линейной пространственно-временной сложности. С функциональным преобразованием вы уже выполняет итерацию или иным образом обращается к элементам массива, поэтому преобразование считается «бесплатным» в смысле постоянной пространственной и временной сложности.

Jason Oster 09.03.2019 05:54

Решение JavaScript для поворота матрицы на 90 градусов на месте:

function rotateBy90(m) {
  var length = m.length;
  //for each layer of the matrix
  for (var first = 0; first < length >> 1; first++) {
    var last = length - 1 - first;
    for (var i = first; i < last; i++) {
      var top = m[first][i]; //store top
      m[first][i] = m[last - i][first]; //top = left
      m[last - i][first] = m[last][last - i]; //left = bottom
      m[last][last - i] = m[i][last]; //bottom = right
      m[i][last] = top; //right = top
    }
  }
  return m;
}

Код C# для поворота [n, m] 2D-массивов на 90 градусов вправо

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

namespace MatrixProject
{
    // mattrix class

    class Matrix{
        private int rows;
        private int cols;
        private int[,] matrix;

        public Matrix(int n){
            this.rows = n;
            this.cols = n;
            this.matrix = new int[this.rows,this.cols];

        }

        public Matrix(int n,int m){
            this.rows = n;
            this.cols = m;

            this.matrix = new int[this.rows,this.cols];
        }

        public void Show()
        {
            for (var i = 0; i < this.rows; i++)
            {
                for (var j = 0; j < this.cols; j++) {
                    Console.Write("{0,3}", this.matrix[i, j]);
                }
                Console.WriteLine();
            }                
        }

        public void ReadElements()
        {
           for (var i = 0; i < this.rows; i++)
                for (var j = 0; j < this.cols; j++)
                {
                    Console.Write("element[{0},{1}] = ",i,j);
                    this.matrix[i, j] = Convert.ToInt32(Console.ReadLine());
                }            
        }


        // rotate [n,m] 2D array by 90 deg right
        public void Rotate90DegRight()
        {

            // create a mirror of current matrix
            int[,] mirror = this.matrix;

            // create a new matrix
            this.matrix = new int[this.cols, this.rows];

            for (int i = 0; i < this.rows; i++)
            {
                for (int j = 0; j < this.cols; j++)
                {
                    this.matrix[j, this.rows - i - 1] = mirror[i, j];
                }
            }

            // replace cols count with rows count
            int tmp = this.rows;
            this.rows = this.cols;
            this.cols = tmp;           
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Matrix myMatrix = new Matrix(3,4);
            Console.WriteLine("Enter matrix elements:");
            myMatrix.ReadElements();
            Console.WriteLine("Matrix elements are:");
            myMatrix.Show();
            myMatrix.Rotate90DegRight();
            Console.WriteLine("Matrix rotated at 90 deg are:");
            myMatrix.Show();
            Console.ReadLine();
        }
    }
}

Результат:

    Enter matrix elements:
    element[0,0]=1
    element[0,1]=2
    element[0,2]=3
    element[0,3]=4
    element[1,0]=5
    element[1,1]=6
    element[1,2]=7
    element[1,3]=8
    element[2,0]=9
    element[2,1]=10
    element[2,2]=11
    element[2,3]=12
    Matrix elements are:
      1  2  3  4
      5  6  7  8
      9 10 11 12
    Matrix rotated at 90 deg are:
      9  5  1
     10  6  2
     11  7  3
     12  8  4

/* 90-degree clockwise:
   temp_array         = left_col
   left_col           = bottom_row
   bottom_row         = reverse(right_col)
   reverse(right_col) = reverse(top_row)
   reverse(top_row)   = temp_array
*/
void RotateClockwise90(int ** arr, int lo, int hi) {

  if (lo >= hi) 
    return;

  for (int i=lo; i<hi; i++) {
    int j = lo+hi-i;
    int temp   = arr[i][lo];
    arr[i][lo] = arr[hi][i];
    arr[hi][i] = arr[j][hi];
    arr[j][hi] = arr[lo][j];
    arr[lo][j] = temp;
  }

  RotateClockwise90(arr, lo+1, hi-1);
}

Поворот на 90 градусов по часовой стрелке с использованием вектора векторов.

 #include<iostream>
 #include<vector>
 #include<algorithm>
 using namespace std;
 //Rotate a Matrix by 90 degrees
void rotateMatrix(vector<vector<int> > &matrix){
   int n=matrix.size();
   for(int i=0;i<n;i++){
    for(int j=i+1;j<n;j++){
        swap(matrix[i][j],matrix[j][i]);
    }
 }
     for(int i=0;i<n;i++){
        reverse(matrix[i].begin(),matrix[i].end());
       }
   }

    int main(){

   int n;
   cout<<"enter the size of the matrix:"<<endl;
     while (cin >> n) {
    vector< vector<int> > m;
      cout<<"enter the elements"<<endl;
    for (int i = 0; i < n; i++) {
        m.push_back(vector<int>(n));
        for (int j = 0; j < n; j++)
            scanf("%d", &m[i][j]);
    }
      cout<<"the rotated matrix is:"<<endl;
      rotateMatrix(m);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++)
            cout << m[i][j] << ' ';
        cout << endl;
    }
   }
   return 0;
 }

Решение Javascript для матрицы NxN со временем выполнения O (N ^ 2) и памятью O (1)

  function rotate90(matrix){
    var length = matrix.length
    for(var row = 0; row < (length / 2); row++){
      for(var col = row; col < ( length - 1 - row); col++){
        var tmpVal = matrix[row][col];
        for(var i = 0; i < 4; i++){
          var rowSwap = col;
          var colSwap = (length - 1) - row;
          var poppedVal = matrix[rowSwap][colSwap];
          matrix[rowSwap][colSwap] = tmpVal;
          tmpVal = poppedVal;
          col = colSwap;
          row = rowSwap;
        }
      }
    }
  }

В наши дни это переоцененный вопрос на собеседовании.

Мое предложение: не позволяйте интервьюеру сбивать вас с толку своим безумным предложением о решении этой проблемы. С помощью доски нарисуйте индексирование входного массива, а затем нарисуйте индексирование выходного массива. Примеры индексации столбцов до и после поворота показаны ниже:

30 --> 00
20 --> 01
10 --> 02
00 --> 03

31 --> 10
21 --> 11
11 --> 12
01 --> 13

Обратите внимание на образец числа после вращения.

Ниже приведено чистое решение для Java. Протестировано, работает:

 Input:
    M A C P 
    B N L D 
    Y E T S 
    I W R Z 

    Output:
    I Y B M 
    W E N A 
    R T L C 
    Z S D P 

/**
 * (c) @author "G A N MOHIM"
 * Oct 3, 2015
 * RotateArrayNintyDegree.java
 */
package rotatearray;

public class RotateArrayNintyDegree {

    public char[][] rotateArrayNinetyDegree(char[][] input) {
        int k; // k is used to generate index for output array

        char[][] output = new char[input.length] [input[0].length];

        for (int i = 0; i < input.length; i++) {
            k = 0;
            for (int j = input.length-1; j >= 0; j--) {
                output[i][k] = input[j][i]; // note how i is used as column index, and j as row
                k++;
            }
        }

        return output;
    }

    public void printArray(char[][] charArray) {
        for (int i = 0; i < charArray.length; i++) {
            for (int j = 0; j < charArray[0].length; j++) {
                System.out.print(charArray[i][j] + " ");
            }
            System.out.println();
        }


    }

    public static void main(String[] args) {
        char[][] input = 
                { {'M', 'A', 'C', 'P'},
                  {'B', 'N', 'L', 'D'},
                  {'Y', 'E', 'T', 'S'},
                  {'I', 'W', 'R', 'Z'}
                };

        char[][] output = new char[input.length] [input[0].length];

        RotateArrayNintyDegree rotationObj = new RotateArrayNintyDegree();
        rotationObj.printArray(input);

        System.out.println("\n");
        output = rotationObj.rotateArrayNinetyDegree(input);
        rotationObj.printArray(output);

    }

}

Решение не на месте.

Sandy 20.11.2015 17:14

Вы можете сделать это в 3 простых шага:

1) Предположим, у нас есть матрица

   1 2 3
   4 5 6
   7 8 9

2) Возьмите транспонирование матрицы

   1 4 7
   2 5 8
   3 6 9

3) Поменяйте местами строки, чтобы получить повернутую матрицу

   3 6 9
   2 5 8
   1 4 7

Исходный код Ява для этого:

public class MyClass {

    public static void main(String args[]) {
        Demo obj = new Demo();
        /*initial matrix to rotate*/
        int[][] matrix = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
        int[][] transpose = new int[3][3]; // matrix to store transpose

        obj.display(matrix);              // initial matrix

        obj.rotate(matrix, transpose);    // call rotate method
        System.out.println();
        obj.display(transpose);           // display the rotated matix
    }
}

class Demo {   
    public void rotate(int[][] mat, int[][] tran) {

        /* First take the transpose of the matrix */
        for (int i = 0; i < mat.length; i++) {
            for (int j = 0; j < mat.length; j++) {
                tran[i][j] = mat[j][i]; 
            }
        }

        /*
         * Interchange the rows of the transpose matrix to get rotated
         * matrix
         */
        for (int i = 0, j = tran.length - 1; i != j; i++, j--) {
            for (int k = 0; k < tran.length; k++) {
                swap(i, k, j, k, tran);
            }
        }
    }

    public void swap(int a, int b, int c, int d, int[][] arr) {
        int temp = arr[a][b];
        arr[a][b] = arr[c][d];
        arr[c][d] = temp;    
    }

    /* Method to display the matrix */
    public void display(int[][] arr) {
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length; j++) {
                System.out.print(arr[i][j] + " ");
            }
            System.out.println();
        }
    }
}

Выход:

1 2 3 
4 5 6 
7 8 9 

3 6 9 
2 5 8 
1 4 7 

Вот это на Java:

public static void rotateInPlace(int[][] m) {
    for(int layer = 0; layer < m.length/2; layer++){
        int first = layer;
        int last = m.length - 1 - first;
        for(int i = first; i < last; i ++){
            int offset = i - first;
            int top = m[first][i];
            m[first][i] = m[last - offset][first];
            m[last - offset][first] = m[last][last - offset];
            m[last][last - offset] = m[i][last];
            m[i][last] = top;
        }
    }
}

Это простой код на языке C для поворота массива на 90 градусов. Надеюсь это поможет.

#include <stdio.h>

void main(){
int arr[3][4] =     {85, 2, 85,  4,
                     85, 6,  7, 85,
                     9, 85, 11, 12};


int arr1[4][3];

int i = 0, j = 0;

for(i=0;i<4;i++){
int k = 2;//k = (number of columns in the new array arr1 - 1)
for(j=0;j<3;j++){
arr1[i][j]=arr[k][i];
k--;
}
}

int l, m;
for(l=0;l<4;l++){
for(m=0;m<3;m++){
printf("%d ", arr1[l][m]);
}
printf("\n");
}
}//end main

Мой пример кода C# для отличного алгоритма, отправленный @dimple:

/* Author: Dudi,
 * http://www.tutorialspoint.com/compile_csharp_online.php?PID=0Bw_CjBb95KQMYm5qU3VjVGNuZFU */

using System.IO;
using System;

class Program
{
    static void Main()
    {
        Console.WriteLine("Rotating this matrix by 90+ degree:");

        int[,] values=new int[3,3]{{1,2,3}, {4,5,6}, {7,8,9}};
        //int[,] values=new int[4,4]{{101,102,103, 104}, {105,106, 107,108}, {109, 110, 111, 112}, {113, 114, 115, 116}};

        print2dArray(ref values);
        transpose2dArray(ref values);
        //print2dArray(ref values);
        reverse2dArray(ref values);
        Console.WriteLine("Output:");
        print2dArray(ref values);
    }

    static void print2dArray(ref int[,] matrix){
        int  nLen = matrix.GetLength(0);
        int  mLen = matrix.GetLength(1);    
        for(int n=0; n<nLen; n++){
            for(int m=0; m<mLen; m++){
                Console.Write(matrix[n,m] +"\t");
            }
            Console.WriteLine();        
        }
        Console.WriteLine();
    }

    static void transpose2dArray(ref int[,] matrix){
        int  nLen = matrix.GetLength(0);
        int  mLen = matrix.GetLength(1);    
        for(int n=0; n<nLen; n++){
            for(int m=0; m<mLen; m++){
                if (n>m){
                    int tmp = matrix[n,m];
                    matrix[n,m] = matrix[m,n];
                    matrix[m,n] = tmp;
                }
            }
        }
    }

    static void reverse2dArray(ref int[,] matrix){
        int  nLen = matrix.GetLength(0);
        int  mLen = matrix.GetLength(1);
        for(int n=0; n<nLen; n++){
            for(int m=0; m<mLen/2; m++){                
                int tmp = matrix[n,m];
                matrix[n,m] = matrix[n, mLen-1-m];
                matrix[n,mLen-1-m] = tmp;
            }
        }
    }
}

/*
Rotating this matrix by 90+ degree:                                                                                                                                             
1       2       3                                                                                                                                                               
4       5       6                                                                                                                                                               
7       8       9                                                                                                                                                               

Output:                                                                                                                                                                         
7       4       1                                                                                                                                                               
8       5       2                                                                                                                                                               
9       6       3  
*/

Я тебя не понял?

Dudi 17.06.2016 05:10

(Сравните версии. Не используйте пробелы и запятые / операторы или не используйте примитив swap.)

greybeard 17.06.2016 10:45

Вот статический универсальный метод C#, который сделает всю работу за вас. Переменные хорошо названы, поэтому вы можете легко уловить идею алгоритма.

private static T[,] Rotate180 <T> (T[,] matrix)
{
    var height = matrix.GetLength (0);
    var width = matrix.GetLength (1);
    var answer = new T[height, width];

    for (int y = 0; y < height / 2; y++)
    {
        int topY = y;
        int bottomY = height - 1 - y;
        for (int topX = 0; topX < width; topX++)
        {
            var bottomX = width - topX - 1;
            answer[topY, topX] = matrix[bottomY, bottomX];
            answer[bottomY, bottomX] = matrix[topY, topX];
        }
    }

    if (height % 2 == 0)
        return answer;

    var centerY = height / 2;
    for (int leftX = 0; leftX < Mathf.CeilToInt(width / 2f); leftX++)
    {
        var rightX = width - 1 - leftX;
        answer[centerY, leftX] = matrix[centerY, rightX];
        answer[centerY, rightX] = matrix[centerY, leftX];
    }

    return answer;
}

    public static void rotateMatrix(int[,] matrix)
    {
        //C#, to rotate an N*N matrix in place
        int n = matrix.GetLength(0);
        int layers =  n / 2;
        int temp, temp2;

        for (int i = 0; i < layers; i++) // for a 5 * 5 matrix, layers will be 2, since at layer three there would be only one element, (2,2), and we do not need to rotate it with itself 
        {
            int offset = 0;
            while (offset < n - 2 * i - 1)
            {
                // top right <- top left 
                temp = matrix[i + offset, n - i - 1]; //top right value when offset is zero
                matrix[i + offset, n - i - 1] = matrix[i, i + offset];   

                //bottom right <- top right 
                temp2 = matrix[n - i - 1, n - i - 1 - offset]; //bottom right value when offset is zero
                matrix[n - i - 1, n - i - 1 - offset] = temp;  

                //bottom left <- bottom right 
                temp = matrix[n - i - 1 - offset, i];
                matrix[n - i - 1 - offset, i] = temp2;  

                //top left <- bottom left 
                matrix[i, i + offset] = temp; 

                offset++;
            }
        }
    }

Отличные ответы, но для тех, кто ищет для этого СУХОЙ код JavaScript - как +90 градусов, так и -90 градусов:

          // Input: 1 2 3
          //        4 5 6
          //        7 8 9

          // Transpose: 
          //       1 4 7
          //       2 5 8
          //       3 6 9

          // Output: 
          // +90 Degree:
          //       7 4 1
          //       8 5 2
          //       9 6 3

          // -90 Degree:
          //      3 6 9
          //      2 5 8
          //      1 4 7

          // Rotate +90
         function rotate90(matrix) {

           matrix = transpose(matrix);
           matrix.map(function(array) {
             array.reverse();
           });

           return matrix;
         }

          // Rotate -90
         function counterRotate90(matrix) {
           var result = createEmptyMatrix(matrix.length);
           matrix = transpose(matrix);
           var counter = 0;

           for (var i = matrix.length - 1; i >= 0; i--) {
             result[counter] = matrix[i];
             counter++;
           }

           return result;
         }

          // Create empty matrix
         function createEmptyMatrix(len) {
           var result = new Array();
           for (var i = 0; i < len; i++) {
             result.push([]);
           }
           return result;
         }

          // Transpose the matrix
         function transpose(matrix) {
           // make empty array
           var len = matrix.length;
           var result = createEmptyMatrix(len);

           for (var i = 0; i < matrix.length; i++) {
             for (var j = 0; j < matrix[i].length; j++) {
               var temp = matrix[i][j];
               result[j][i] = temp;
             }
           }
           return result;
         }



          // Test Cases
         var array1 = [
           [1, 2],
           [3, 4]
         ];
         var array2 = [
           [1, 2, 3],
           [4, 5, 6],
           [7, 8, 9]
         ];
         var array3 = [
           [1, 2, 3, 4],
           [5, 6, 7, 8],
           [9, 10, 11, 12],
           [13, 14, 15, 16]
         ];

          // +90 degress Rotation Tests

         var test1 = rotate90(array1);
         var test2 = rotate90(array2);
         var test3 = rotate90(array3);
         console.info(test1);
         console.info(test2);
         console.info(test3);

          // -90 degress Rotation Tests
         var test1 = counterRotate90(array1);
         var test2 = counterRotate90(array2);
         var test3 = counterRotate90(array3);
         console.info(test1);
         console.info(test2);
         console.info(test3);

Можно довольно чисто рекурсивно сделать, вот моя реализация на голанге!

рекурсивно вращать матрицу nxn в go golang без дополнительной памяти

func rot90(a [][]int) {
    n := len(a)
    if n == 1 {
        return
    }
    for i := 0; i < n; i++ {
        a[0][i], a[n-1-i][n-1] = a[n-1-i][n-1], a[0][i]
    }
    rot90(a[1:])
}

В Java

public class Matrix {
/* Author Shrikant Dande */
private static void showMatrix(int[][] arr,int rows,int col){

    for(int i =0 ;i<rows;i++){
        for(int j =0 ;j<col;j++){
            System.out.print(arr[i][j]+" ");
        }
        System.out.println();
    }

}

private static void rotateMatrix(int[][] arr,int rows,int col){

    int[][] tempArr = new int[4][4];
    for(int i =0 ;i<rows;i++){
        for(int j =0 ;j<col;j++){
            tempArr[i][j] = arr[rows-1-j][i];
            System.out.print(tempArr[i][j]+" ");
        }
        System.out.println();
    }

}
public static void main(String[] args) {
    int[][] arr = { {1,  2,  3,  4},
             {5,  6,  7,  8},
             {9,  1, 2, 5},
             {7, 4, 8, 9}};
    int rows = 4,col = 4;

    showMatrix(arr, rows, col);
    System.out.println("------------------------------------------------");
    rotateMatrix(arr, rows, col);

}

}

Вот решение Javascript:

const transpose = m => m[0].map((x,i) => m.map(x => x[i]));

a: // original matrix
123
456
789

transpose(a).reverse(); // rotate 90 degrees counter clockwise 
369
258
147

transpose(a.slice().reverse()); // rotate 90 degrees clockwise 
741
852
963

transpose(transpose(a.slice().reverse()).slice().reverse())
// rotate 180 degrees 
987
654
321

Попробуйте Моя библиотека AbacusUtil:

@Test
public void test_42519() throws Exception {
    final IntMatrix matrix = IntMatrix.range(0, 16).reshape(4);

    N.println("======= original ====================== = ");
    matrix.println();
    // print out:
    //    [0, 1, 2, 3]
    //    [4, 5, 6, 7]
    //    [8, 9, 10, 11]
    //    [12, 13, 14, 15]

    N.println("======= rotate 90 ===================== = ");
    matrix.rotate90().println();
    // print out:
    //    [12, 8, 4, 0]
    //    [13, 9, 5, 1]
    //    [14, 10, 6, 2]
    //    [15, 11, 7, 3]

    N.println("======= rotate 180 ==================== = ");
    matrix.rotate180().println();
    // print out:
    //    [15, 14, 13, 12]
    //    [11, 10, 9, 8]
    //    [7, 6, 5, 4]
    //    [3, 2, 1, 0]

    N.println("======= rotate 270 ===================== = ");
    matrix.rotate270().println();
    // print out:
    //    [3, 7, 11, 15]
    //    [2, 6, 10, 14]
    //    [1, 5, 9, 13]
    //    [0, 4, 8, 12]

    N.println("======= transpose ====================== = ");
    matrix.transpose().println();
    // print out:
    //    [0, 4, 8, 12]
    //    [1, 5, 9, 13]
    //    [2, 6, 10, 14]
    //    [3, 7, 11, 15]

    final IntMatrix bigMatrix = IntMatrix.range(0, 10000_0000).reshape(10000);

    // It take about 2 seconds to rotate 10000 X 10000 matrix.
    Profiler.run(1, 2, 3, "sequential", () -> bigMatrix.rotate90()).printResult();

    // Want faster? Go parallel. 1 second to rotate 10000 X 10000 matrix.
    final int[][] a = bigMatrix.array();
    final int[][] c = new int[a[0].length][a.length];
    final int n = a.length;
    final int threadNum = 4;

    Profiler.run(1, 2, 3, "parallel", () -> {
        IntStream.range(0, n).parallel(threadNum).forEach(i -> {
            for (int j = 0; j < n; j++) {
                c[i][j] = a[n - j - 1][i];
            }
        });
    }).printResult();
}

Решение PHP по часовой стрелке и против часовой стрелки

$aMatrix = array(
    array( 1, 2, 3 ),
    array( 4, 5, 6 ),
    array( 7, 8, 9 )
    );

function CounterClockwise( $aMatrix )
{
    $iCount  = count( $aMatrix );
    $aReturn = array();
    for( $y = 0; $y < $iCount; ++$y )
    {
        for( $x = 0; $x < $iCount; ++$x )
        {
            $aReturn[ $iCount - $x - 1 ][ $y ] = $aMatrix[ $y ][ $x ];
        }
    }
    return $aReturn;
}

function Clockwise( $aMatrix )
{
    $iCount  = count( $aMatrix );
    $aReturn = array();
    for( $y = 0; $y < $iCount; ++$y )
    {
        for( $x = 0; $x < $iCount; ++$x )
        {
            $aReturn[ $x ][ $iCount - $y - 1 ] = $aMatrix[ $y ][ $x ];
        }
    }
    return $aReturn;
}

function printMatrix( $aMatrix )
{
    $iCount = count( $aMatrix );
    for( $x = 0; $x < $iCount; ++$x )
    {
        for( $y = 0; $y < $iCount; ++$y )
        {
            echo $aMatrix[ $x ][ $y ];
            echo " ";
        }
        echo "\n";
    }
}
printMatrix( $aMatrix );
echo "\n";
$aNewMatrix = CounterClockwise( $aMatrix );
printMatrix( $aNewMatrix );
echo "\n";
$aNewMatrix = Clockwise( $aMatrix );
printMatrix( $aNewMatrix );

Код C для транспонирования и поворота матрицы (+/- 90, +/- 180)

  • Поддерживает квадратные и неквадратные матрицы, имеет функции копирования и копирования
  • Поддерживает как 2D-массивы, так и одномерные указатели с логическими строками / столбцами
  • Модульные тесты; см. тесты для примеров использования
  • протестировал gcc -std = c90 -Wall -pedantic, MSVC17

`

#include <stdlib.h>
#include <memory.h>
#include <assert.h>

/* 
    Matrix transpose & rotate (+/-90, +/-180)
        Supports both 2D arrays and 1D pointers with logical rows/cols
        Supports square and non-square matrices, has in-place and copy features
        See tests for examples of usage
    tested gcc -std=c90 -Wall -pedantic, MSVC17
*/

typedef int matrix_data_t;  /* matrix data type */

void transpose(const matrix_data_t* src, matrix_data_t* dst, int rows, int cols);
void transpose_inplace(matrix_data_t* data, int n );
void rotate(int direction, const matrix_data_t* src, matrix_data_t* dst, int rows, int cols);
void rotate_inplace(int direction, matrix_data_t* data, int n);
void reverse_rows(matrix_data_t* data, int rows, int cols);
void reverse_cols(matrix_data_t* data, int rows, int cols);

/* test/compare fn */
int test_cmp(const matrix_data_t* lhs, const matrix_data_t* rhs, int rows, int cols );

/* TESTS/USAGE */
void transpose_test() {

    matrix_data_t sq3x3[9] = { 0,1,2,3,4,5,6,7,8 };/* 3x3 square, odd length side */
    matrix_data_t sq3x3_cpy[9];
    matrix_data_t sq3x3_2D[3][3] = { { 0,1,2 },{ 3,4,5 },{ 6,7,8 } };/* 2D 3x3 square */
    matrix_data_t sq3x3_2D_copy[3][3];

    /* expected test values */
    const matrix_data_t sq3x3_orig[9] = { 0,1,2,3,4,5,6,7,8 };
    const matrix_data_t sq3x3_transposed[9] = { 0,3,6,1,4,7,2,5,8};

    matrix_data_t sq4x4[16]= { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };/* 4x4 square, even length*/
    const matrix_data_t sq4x4_orig[16] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
    const matrix_data_t sq4x4_transposed[16] = { 0,4,8,12,1,5,9,13,2,6,10,14,3,7,11,15 };

    /* 2x3 rectangle */
    const matrix_data_t r2x3_orig[6] = { 0,1,2,3,4,5 };
    const matrix_data_t r2x3_transposed[6] = { 0,3,1,4,2,5 };
    matrix_data_t r2x3_copy[6];

    matrix_data_t r2x3_2D[2][3] = { {0,1,2},{3,4,5} };  /* 2x3 2D rectangle */
    matrix_data_t r2x3_2D_t[3][2];

    /* matrix_data_t r3x2[6] = { 0,1,2,3,4,5 }; */
    matrix_data_t r3x2_copy[6];
    /* 3x2 rectangle */
    const matrix_data_t r3x2_orig[6] = { 0,1,2,3,4,5 };
    const matrix_data_t r3x2_transposed[6] = { 0,2,4,1,3,5 };

    matrix_data_t r6x1[6] = { 0,1,2,3,4,5 };    /* 6x1 */
    matrix_data_t r6x1_copy[6];

    matrix_data_t r1x1[1] = { 0 };  /*1x1*/
    matrix_data_t r1x1_copy[1];

    /* 3x3 tests, 2D array tests */
    transpose_inplace(sq3x3, 3);    /* transpose in place */
    assert(!test_cmp(sq3x3, sq3x3_transposed, 3, 3));
    transpose_inplace(sq3x3, 3);    /* transpose again */
    assert(!test_cmp(sq3x3, sq3x3_orig, 3, 3));

    transpose(sq3x3, sq3x3_cpy, 3, 3);  /* transpose copy 3x3*/
    assert(!test_cmp(sq3x3_cpy, sq3x3_transposed, 3, 3));

    transpose((matrix_data_t*)sq3x3_2D, (matrix_data_t*)sq3x3_2D_copy, 3, 3);   /* 2D array transpose/copy */
    assert(!test_cmp((matrix_data_t*)sq3x3_2D_copy, sq3x3_transposed, 3, 3));
    transpose_inplace((matrix_data_t*)sq3x3_2D_copy, 3);    /* 2D array transpose in place */
    assert(!test_cmp((matrix_data_t*)sq3x3_2D_copy, sq3x3_orig, 3, 3));

    /* 4x4 tests */
    transpose_inplace(sq4x4, 4);    /* transpose in place */
    assert(!test_cmp(sq4x4, sq4x4_transposed, 4,4));
    transpose_inplace(sq4x4, 4);    /* transpose again */
    assert(!test_cmp(sq4x4, sq4x4_orig, 3, 3));

    /* 2x3,3x2 tests */
    transpose(r2x3_orig, r2x3_copy, 2, 3);
    assert(!test_cmp(r2x3_copy, r2x3_transposed, 3, 2));

    transpose(r3x2_orig, r3x2_copy, 3, 2);
    assert(!test_cmp(r3x2_copy, r3x2_transposed, 2,3));

    /* 2D array */
    transpose((matrix_data_t*)r2x3_2D, (matrix_data_t*)r2x3_2D_t, 2, 3);
    assert(!test_cmp((matrix_data_t*)r2x3_2D_t, r2x3_transposed, 3,2));

    /* Nx1 test, 1x1 test */
    transpose(r6x1, r6x1_copy, 6, 1);
    assert(!test_cmp(r6x1_copy, r6x1, 1, 6));

    transpose(r1x1, r1x1_copy, 1, 1);
    assert(!test_cmp(r1x1_copy, r1x1, 1, 1));

}

void rotate_test() {

    /* 3x3 square */
    const matrix_data_t sq3x3[9] = { 0,1,2,3,4,5,6,7,8 };
    const matrix_data_t sq3x3_r90[9] = { 6,3,0,7,4,1,8,5,2 };
    const matrix_data_t sq3x3_180[9] = { 8,7,6,5,4,3,2,1,0 };
    const matrix_data_t sq3x3_l90[9] = { 2,5,8,1,4,7,0,3,6 };
    matrix_data_t sq3x3_copy[9];

    /* 3x3 square, 2D */
    matrix_data_t sq3x3_2D[3][3] = { { 0,1,2 },{ 3,4,5 },{ 6,7,8 } };

    /* 4x4, 2D */
    matrix_data_t sq4x4[4][4] = { { 0,1,2,3 },{ 4,5,6,7 },{ 8,9,10,11 },{ 12,13,14,15 } };
    matrix_data_t sq4x4_copy[4][4];
    const matrix_data_t sq4x4_r90[16] = { 12,8,4,0,13,9,5,1,14,10,6,2,15,11,7,3 };
    const matrix_data_t sq4x4_l90[16] = { 3,7,11,15,2,6,10,14,1,5,9,13,0,4,8,12 };
    const matrix_data_t sq4x4_180[16] = { 15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0 };

    matrix_data_t r6[6] = { 0,1,2,3,4,5 };  /* rectangle with area of 6 (1x6,2x3,3x2, or 6x1) */
    matrix_data_t r6_copy[6];
    const matrix_data_t r1x6_r90[6] = { 0,1,2,3,4,5 };
    const matrix_data_t r1x6_l90[6] = { 5,4,3,2,1,0 };
    const matrix_data_t r1x6_180[6] = { 5,4,3,2,1,0 };

    const matrix_data_t r2x3_r90[6] = { 3,0,4,1,5,2 };
    const matrix_data_t r2x3_l90[6] = { 2,5,1,4,0,3 };
    const matrix_data_t r2x3_180[6] = { 5,4,3,2,1,0 };

    const matrix_data_t r3x2_r90[6] = { 4,2,0,5,3,1 };
    const matrix_data_t r3x2_l90[6] = { 1,3,5,0,2,4 };
    const matrix_data_t r3x2_180[6] = { 5,4,3,2,1,0 };

    const matrix_data_t r6x1_r90[6] = { 5,4,3,2,1,0 };
    const matrix_data_t r6x1_l90[6] = { 0,1,2,3,4,5 };
    const matrix_data_t r6x1_180[6] = { 5,4,3,2,1,0 };

    /* sq3x3 tests */
    rotate(90, sq3x3, sq3x3_copy, 3, 3);    /* +90 */
    assert(!test_cmp(sq3x3_copy, sq3x3_r90, 3, 3));
    rotate(-90, sq3x3, sq3x3_copy, 3, 3);   /* -90 */
    assert(!test_cmp(sq3x3_copy, sq3x3_l90, 3, 3));
    rotate(180, sq3x3, sq3x3_copy, 3, 3);   /* 180 */
    assert(!test_cmp(sq3x3_copy, sq3x3_180, 3, 3));
    /* sq3x3 in-place rotations */
    memcpy( sq3x3_copy, sq3x3, 3 * 3 * sizeof(matrix_data_t));
    rotate_inplace(90, sq3x3_copy, 3);
    assert(!test_cmp(sq3x3_copy, sq3x3_r90, 3, 3));
    rotate_inplace(-90, sq3x3_copy, 3);
    assert(!test_cmp(sq3x3_copy, sq3x3, 3, 3)); /* back to 0 orientation */
    rotate_inplace(180, sq3x3_copy, 3);
    assert(!test_cmp(sq3x3_copy, sq3x3_180, 3, 3));
    rotate_inplace(-180, sq3x3_copy, 3);
    assert(!test_cmp(sq3x3_copy, sq3x3, 3, 3));
    rotate_inplace(180, (matrix_data_t*)sq3x3_2D, 3);/* 2D test */
    assert(!test_cmp((matrix_data_t*)sq3x3_2D, sq3x3_180, 3, 3));

    /* sq4x4 */
    rotate(90, (matrix_data_t*)sq4x4, (matrix_data_t*)sq4x4_copy, 4, 4);
    assert(!test_cmp((matrix_data_t*)sq4x4_copy, sq4x4_r90, 4, 4));
    rotate(-90, (matrix_data_t*)sq4x4, (matrix_data_t*)sq4x4_copy, 4, 4);
    assert(!test_cmp((matrix_data_t*)sq4x4_copy, sq4x4_l90, 4, 4));
    rotate(180, (matrix_data_t*)sq4x4, (matrix_data_t*)sq4x4_copy, 4, 4);
    assert(!test_cmp((matrix_data_t*)sq4x4_copy, sq4x4_180, 4, 4));

    /* r6 as 1x6 */
    rotate(90, r6, r6_copy, 1, 6);
    assert(!test_cmp(r6_copy, r1x6_r90, 1, 6));
    rotate(-90, r6, r6_copy, 1, 6);
    assert(!test_cmp(r6_copy, r1x6_l90, 1, 6));
    rotate(180, r6, r6_copy, 1, 6);
    assert(!test_cmp(r6_copy, r1x6_180, 1, 6));

    /* r6 as 2x3 */
    rotate(90, r6, r6_copy, 2, 3);
    assert(!test_cmp(r6_copy, r2x3_r90, 2, 3));
    rotate(-90, r6, r6_copy, 2, 3);
    assert(!test_cmp(r6_copy, r2x3_l90, 2, 3));
    rotate(180, r6, r6_copy, 2, 3);
    assert(!test_cmp(r6_copy, r2x3_180, 2, 3));

    /* r6 as 3x2 */
    rotate(90, r6, r6_copy, 3, 2);
    assert(!test_cmp(r6_copy, r3x2_r90, 3, 2));
    rotate(-90, r6, r6_copy, 3, 2);
    assert(!test_cmp(r6_copy, r3x2_l90, 3, 2));
    rotate(180, r6, r6_copy, 3, 2);
    assert(!test_cmp(r6_copy, r3x2_180, 3, 2));

    /* r6 as 6x1 */
    rotate(90, r6, r6_copy, 6, 1);
    assert(!test_cmp(r6_copy, r6x1_r90, 6, 1));
    rotate(-90, r6, r6_copy, 6, 1);
    assert(!test_cmp(r6_copy, r6x1_l90, 6, 1));
    rotate(180, r6, r6_copy, 6, 1);
    assert(!test_cmp(r6_copy, r6x1_180, 6, 1));
}

/* test comparison fn, return 0 on match else non zero */
int test_cmp(const matrix_data_t* lhs, const matrix_data_t* rhs, int rows, int cols) {

    int r, c;

    for (r = 0; r < rows; ++r) {
        for (c = 0; c < cols; ++c) {
            if ((lhs + r * cols)[c] != (rhs + r * cols)[c])
                return -1;
        }
    }
    return 0;
}

/*
Reverse values in place of each row in 2D matrix data[rows][cols] or in 1D pointer with logical rows/cols
[A B C] ->  [C B A]
[D E F]     [F E D]
*/
void reverse_rows(matrix_data_t* data, int rows, int cols) {

    int r, c;
    matrix_data_t temp;
    matrix_data_t* pRow = NULL;

    for (r = 0; r < rows; ++r) {
        pRow = (data + r * cols);
        for (c = 0; c < (int)(cols / 2); ++c) { /* explicit truncate */
            temp = pRow[c];
            pRow[c] = pRow[cols - 1 - c];
            pRow[cols - 1 - c] = temp;
        }
    }
}

/*
Reverse values in place of each column in 2D matrix data[rows][cols] or in 1D pointer with logical rows/cols
[A B C] ->  [D E F]
[D E F]     [A B C]
*/
void reverse_cols(matrix_data_t* data, int rows, int cols) {

    int r, c;
    matrix_data_t temp;
    matrix_data_t* pRowA = NULL;
    matrix_data_t* pRowB = NULL;

    for (c = 0; c < cols; ++c) {
        for (r = 0; r < (int)(rows / 2); ++r) { /* explicit truncate */
            pRowA = data + r * cols;
            pRowB = data + cols * (rows - 1 - r);
            temp = pRowA[c];
            pRowA[c] = pRowB[c];
            pRowB[c] = temp;
        }
    }
}

/* Transpose NxM matrix to MxN matrix in O(n) time */
void transpose(const matrix_data_t* src, matrix_data_t* dst, int N, int M) {

    int i;
    for (i = 0; i<N*M; ++i) dst[(i%M)*N + (i / M)] = src[i];    /* one-liner version */

    /*
    expanded version of one-liner:  calculate XY based on array index, then convert that to YX array index
    int i,j,x,y;
    for (i = 0; i < N*M; ++i) {
    x = i % M;
    y = (int)(i / M);
    j = x * N + y;
    dst[j] = src[i];
    }
    */

    /*
    nested for loop version
    using ptr arithmetic to get proper row/column
    this is really just dst[col][row]=src[row][col]

    int r, c;

    for (r = 0; r < rows; ++r) {
        for (c = 0; c < cols; ++c) {
            (dst + c * rows)[r] = (src + r * cols)[c];
        }
    }
    */
}

/*
Transpose NxN matrix in place
*/
void transpose_inplace(matrix_data_t* data, int N ) {

    int r, c;
    matrix_data_t temp;

    for (r = 0; r < N; ++r) {
        for (c = r; c < N; ++c) { /*start at column=row*/
                                    /* using ptr arithmetic to get proper row/column */
                                    /* this is really just
                                    temp=dst[col][row];
                                    dst[col][row]=src[row][col];
                                    src[row][col]=temp;
                                    */
            temp = (data + c * N)[r];
            (data + c * N)[r] = (data + r * N)[c];
            (data + r * N)[c] = temp;
        }
    }
}

/*
Rotate 1D or 2D src matrix to dst matrix in a direction (90,180,-90)
Precondition:  src and dst are 2d matrices with dimensions src[rows][cols] and dst[cols][rows] or 1D pointers with logical rows/cols
*/
void rotate(int direction, const matrix_data_t* src, matrix_data_t* dst, int rows, int cols) {

    switch (direction) {
    case -90:
        transpose(src, dst, rows, cols);
        reverse_cols(dst, cols, rows);
        break;
    case 90:
        transpose(src, dst, rows, cols);
        reverse_rows(dst, cols, rows);
        break;
    case 180:
    case -180:
        /* bit copy to dst, use in-place reversals */
        memcpy(dst, src, rows*cols*sizeof(matrix_data_t));
        reverse_cols(dst, cols, rows);
        reverse_rows(dst, cols, rows);
        break;
    }
}

/*
Rotate array in a direction.
Array must be NxN 2D or 1D array with logical rows/cols
Direction can be (90,180,-90,-180)
*/
void rotate_inplace( int direction, matrix_data_t* data, int n) {

    switch (direction) {
    case -90:
        transpose_inplace(data, n);
        reverse_cols(data, n, n);
        break;
    case 90:
        transpose_inplace(data, n);
        reverse_rows(data, n, n);
        break;
    case 180:
    case -180:
        reverse_cols(data, n, n);
        reverse_rows(data, n, n);
        break;
    }
}

`

Основываясь на множестве других ответов, я придумал это на C#:

/// <param name = "rotation">The number of rotations (if negative, the <see cref = "Matrix{TValue}"/> is rotated counterclockwise; 
/// otherwise, it's rotated clockwise). A single (positive) rotation is equivalent to 90° or -270°; a single (negative) rotation is 
/// equivalent to -90° or 270°. Matrices may be rotated by 90°, 180°, or 270° only (or multiples thereof).</param>
/// <returns></returns>
public Matrix<TValue> Rotate(int rotation)
{
    var result = default(Matrix<TValue>);

    //This normalizes the requested rotation (for instance, if 10 is specified, the rotation is actually just +-2 or +-180°, but all 
    //correspond to the same rotation).
    var d = rotation.ToDouble() / 4d;
    d = d - (int)d;

    var degree = (d - 1d) * 4d;

    //This gets the type of rotation to make; there are a total of four unique rotations possible (0°, 90°, 180°, and 270°).
    //Each correspond to 0, 1, 2, and 3, respectively (or 0, -1, -2, and -3, if in the other direction). Since
    //1 is equivalent to -3 and so forth, we combine both cases into one. 
    switch (degree)
    {
        case -3:
        case +1:
            degree = 3;
            break;
        case -2:
        case +2:
            degree = 2;
            break;
        case -1:
        case +3:
            degree = 1;
            break;
        case -4:
        case  0:
        case +4:
            degree = 0;
            break;
    }
    switch (degree)
    {
        //The rotation is 0, +-180°
        case 0:
        case 2:
            result = new TValue[Rows, Columns];
            break;
        //The rotation is +-90°
        case 1:
        case 3:
            result = new TValue[Columns, Rows];
            break;
    }

    for (uint i = 0; i < Columns; ++i)
    {
        for (uint j = 0; j < Rows; ++j)
        {
            switch (degree)
            {
                //If rotation is 0°
                case 0:
                    result._values[j][i] = _values[j][i];
                    break;
                //If rotation is -90°
                case 1:
                    //Transpose, then reverse each column OR reverse each row, then transpose
                    result._values[i][j] = _values[j][Columns - i - 1];
                    break;
                //If rotation is +-180°
                case 2:
                    //Reverse each column, then reverse each row
                    result._values[(Rows - 1) - j][(Columns - 1) - i] = _values[j][i];
                    break;
                //If rotation is +90°
                case 3:
                    //Transpose, then reverse each row
                    result._values[i][j] = _values[Rows - j - 1][i];
                    break;
            }
        }
    }
    return result;
}

Где _values соответствует частному двумерному массиву, определенному Matrix<TValue> (в форме [][]). result = new TValue[Columns, Rows] возможен через неявную перегрузку оператора и преобразует двумерный массив в Matrix<TValue>. Два свойства Columns и Rows - это общедоступные свойства, которые получают количество столбцов и строк текущего экземпляра:

public uint Columns 
    => (uint)_values[0].Length;

public uint Rows 
    => (uint)_values.Length;

При условии, конечно, что вы предпочитаете работать с беззнаковыми индексами ;-)

Все это позволяет вам указать, сколько раз он должен быть повернут и должен ли он быть повернут влево (если меньше нуля) или вправо (если больше нуля). Вы можете улучшить это, чтобы проверить вращение в фактических градусах, но тогда вы захотите сгенерировать исключение, если значение не кратно 90. С этим вводом вы можете соответствующим образом изменить метод:

public Matrix<TValue> Rotate(int rotation)
{
    var _rotation = (double)rotation / 90d;

    if (_rotation - Math.Floor(_rotation) > 0)
    {
        throw new NotSupportedException("A matrix may only be rotated by multiples of 90.").
    }

    rotation = (int)_rotation;
    ...
}

Поскольку степень более точно выражается double, чем int, но матрица может вращаться только кратно 90, гораздо более интуитивно понятно, чтобы аргумент соответствовал чему-то еще, что может быть точно представлено используемой структурой данных. int идеален, потому что он может сказать вам, сколько раз повернуть его до определенной единицы (90), а также направление. double вполне может сказать вам это, но он также включает значения, которые не поддерживаются этой операцией (что по своей сути противоречит интуиции).

На основе алгоритма вики сообщества и этот ТАК ответ для транспонирования массивов, вот версия Swift 4 для поворота некоторого 2D-массива на 90 градусов против часовой стрелки. Предполагается, что matrix представляет собой 2D-массив:

func rotate(matrix: [[Int]]) -> [[Int]] {
    let transposedPoints = transpose(input: matrix)
    let rotatedPoints = transposedPoints.map{ Array($0.reversed()) }
    return rotatedPoints
}


fileprivate func transpose<T>(input: [[T]]) -> [[T]] {
    if input.isEmpty { return [[T]]() }
    let count = input[0].count
    var out = [[T]](repeating: [T](), count: count)
    for outer in input {
        for (index, inner) in outer.enumerated() {
            out[index].append(inner)
        }
    }

    return out
}

Это решение не заботится о размерах квадрата или прямоугольника, вы можете вращать 4x5, 5x4 или даже 4x4, его также не волнует размер. Обратите внимание, что эта реализация создает новый массив каждый раз, когда вы вызываете метод rotate90, он вообще не изменяет исходный массив.

public static void main(String[] args) {
    int[][] a = new int[][] { 
                    { 1, 2, 3, 4 }, 
                    { 5, 6, 7, 8 }, 
                    { 9, 0, 1, 2 }, 
                    { 3, 4, 5, 6 }, 
                    { 7, 8, 9, 0 } 
                  };
    int[][] rotate180 = rotate90(rotate90(a));
    print(rotate180);
}

static int[][] rotate90(int[][] a) {
    int[][] ret = new int[a[0].length][a.length];
    for (int i = 0; i < a.length; i++) {
        for (int j = 0; j < a[i].length; j++) {
            ret[j][a.length - i - 1] = a[i][j];
        }
    }
    return ret;
}

static void print(int[][] array) {
    for (int i = 0; i < array.length; i++) {
        System.out.print("[");
        for (int j = 0; j < array[i].length; j++) {
            System.out.print(array[i][j]);
            System.out.print(" ");
        }
        System.out.println("]");
    }
}

Я смог сделать это с помощью одиночная петля. Временная сложность выглядит как OK), где K - все элементы массива. Вот как я это сделал в JavaScript:

Во-первых, мы представляем матрицу n ^ 2 одним массивом. Затем выполните итерацию следующим образом:

/**
 * Rotates matrix 90 degrees clockwise
 * @param arr: the source array
 * @param n: the array side (array is square n^2)
 */
function rotate (arr, n) {
  var rotated = [], indexes = []

  for (var i = 0; i < arr.length; i++) {
    if (i < n)
      indexes[i] = i * n + (n - 1)
    else
      indexes[i] = indexes[i - n] - 1

    rotated[indexes[i]] = arr[i]
  }
  return rotated
}

По сути, мы преобразуем индексы исходного массива:

[0,1,2,3,4,5,6,7,8] => [2,5,8,1,4,7,0,3,6]

Затем, используя этот преобразованный массив indexes, мы помещаем фактические значения в окончательный массив rotated.

Вот несколько тестовых примеров:

//n=3
rotate([
  1, 2, 3,
  4, 5, 6,
  7, 8, 9], 3))

//result:
[7, 4, 1,
 8, 5, 2,
 9, 6, 3]


//n=4
rotate([
  1,  2,  3,  4,
  5,  6,  7,  8,
  9,  10, 11, 12,
  13, 14, 15, 16], 4))

//result:
[13,  9,  5,  1,
 14, 10,  6,  2,
 15, 11,  7,  3,
 16, 12,  8,  4]


//n=5
rotate([
  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], 5))

//result:
[21, 16, 11,  6,  1, 
 22, 17, 12,  7,  2, 
 23, 18, 13,  8,  3, 
 24, 19, 14,  9,  4, 
 25, 20, 15, 10,  5]

Технически это по-прежнему n ^ 2, поскольку k = n ^ 2. Но отличная идея, и я почти уверен, что n ^ 2 - это минимальная временная сложность для поворота 2d-массива.

Yolomep 04.10.2020 05:12

В Эйген (C++):

Eigen::Matrix2d mat;
mat <<  1, 2,
        3, 4;
std::cout << mat << "\n\n";

Eigen::Matrix2d r_plus_90 = mat.transpose().rowwise().reverse();
std::cout << r_plus_90 << "\n\n";

Eigen::Matrix2d r_minus_90 = mat.transpose().colwise().reverse();
std::cout << r_minus_90 << "\n\n";

Eigen::Matrix2d r_180 = mat.colwise().reverse().rowwise().reverse(); // +180 same as -180
std::cout << r_180 << "\n\n";

Выход:

1 2
3 4

3 1
4 2

2 4
1 3

4 3
2 1

Распространенный метод поворота 2D-массива по или против часовой стрелки.

  • по часовой стрелке вращать
    • сначала переверните вверх вниз, затем поменяйте местами симметрию
      1 2 3     7 8 9     7 4 1
      4 5 6  => 4 5 6  => 8 5 2
      7 8 9     1 2 3     9 6 3
      
void rotate(vector<vector<int> > &matrix) {
    reverse(matrix.begin(), matrix.end());
    for (int i = 0; i < matrix.size(); ++i) {
        for (int j = i + 1; j < matrix[i].size(); ++j)
            swap(matrix[i][j], matrix[j][i]);
    }
}
  • вращать против часовой стрелки
    • сначала переверните слева направо, затем поменяйте местами симметрию
      1 2 3     3 2 1     3 6 9
      4 5 6  => 6 5 4  => 2 5 8
      7 8 9     9 8 7     1 4 7
      
void anti_rotate(vector<vector<int> > &matrix) {
    for (auto vi : matrix) reverse(vi.begin(), vi.end());
    for (int i = 0; i < matrix.size(); ++i) {
        for (int j = i + 1; j < matrix[i].size(); ++j)
            swap(matrix[i][j], matrix[j][i]);
    }
}

Мне нравится это решение, потому что оно довольно интуитивно понятное и простое, спасибо

BdR 15.02.2021 01:38

Другие вопросы по теме