Вдохновленный Сообщение Раймонда Чена, допустим, у вас есть двумерный массив 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?
См. Также stackoverflow.com/questions/848025/rotating-bitmaps-in-code
Задача этого вопроса (в рассказе Раймонда) заключалась в том, чтобы просто убедиться, что у кандидата есть определенный набор базовых способностей. Решение n ^ 2 в порядке.
Какой твой? Вы не говорите, является ли 2D-массив квадратным (это не в общем случае! Например, вектор - это матрица с одним размером 1), но вы, кажется, подразумеваете, что n - это ширина и высота, и, следовательно, имеет n² элементов. . Было бы разумнее иметь n как количество элементов, где n = w × h.
Согласен с niXar. То, что N сильно коррелирует с квадратом некоторого другого значения, не делает решение N ^ 2.
У меня нет времени писать об этом, но предпочтительный способ выполнения вращения - использовать кватернионы.
Было бы сложнее, если бы его размер был NxM вместо NxN.
Обратите внимание, что для больших массивов промахи в кеше могут быть проблематичными, поэтому вы захотите использовать тайлинг, как это предлагается в ответе на stackoverflow.com/questions/848025/rotating-bitmaps-in-code. В результате вы получите четырехкратный вложенный цикл.
Вот быстрый способ сделать это: сохранить индексы строки и столбца (скажем, i и j). Транспонирование занимает постоянное время (просто поменяйте индексы местами :). То же самое можно проделать и с поворотами (поиграйте с индексами).
В случае, если n ^ 2 невозможно. Вы можете создать интерфейс для доступа к каждому элементу. Затем, учитывая (i, j), примените вращение к (i, j), получите доступ к повернутому элементу и вернитесь. Может быть, не лучшее решение, но работает.
Я думаю, что вы все еще можете сделать это меньше, чем N ^ 2. Однако требуется более сложная структура данных. Может быть, какой-то тип двусвязного списка?
@erikkallen может быть сжат?
Я сделал это одним циклом: stackoverflow.com/a/61812224/996926





Вот он на 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)?
Ваше решение имеет сложность пространства O (n ^ 2). Нужно делать лучше
Как насчет матрицы N X M?
Сложность линейна по количеству элементов в массиве. Если N - количество элементов, сложность O (N). Если N - длина стороны, то да, сложность O (N ^ 2), но это все еще оптимально. Вы должны прочитать каждый элемент хотя бы один раз. Печать матрицы такая же сложность
Здесь добавлен ответ для матрицы N x M: stackoverflow.com/a/38027015/1322703
@KshitijJain, как это сложность пространства O (n ^ 2)? Он дублирует массив, так разве это не O (n) пространство?
Для поворота на -90 градусов: ret[i][j] = matrix[j][n - i - 1]
Поправьте меня, если я ошибаюсь, но разве параллелизм (при условии, что система имеет разумное количество ядер) не может уменьшить сложность, хотя бы немного?
Смотрите мой ответ одним циклом: stackoverflow.com/a/61812224/996926
Вот моя версия 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 - еще один, предположительно превосходящий алгоритм транспонирования на месте.
Я согласен с этим. Имейте метод, который определяет перевод между исходными данными и "повернутыми" данными.
Как я сказал в своем предыдущем посте, вот некоторый код на 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: наверное, потому что он в 5 раз больше
@Toad: написание кода - это всегда компромисс между конкурирующими требованиями: скоростью, размером, стоимостью и т. д.
правда ... еще одна проблема заключается в том, что матрица на самом деле не поворачивается, а вращается «точно по времени». Это отлично подходит для доступа к нескольким элементам, но было бы ужасно, если бы эта матрица использовалась в вычислениях или манипуляциях с изображениями. Так что говорить O (1) не совсем честно.
@Toad вы тестировали его, чтобы сделать какое-нибудь заявление о том, что это ужасно? Это решение можно оптимизировать, удалив вызовы m_matrix.GetUpperBound() и коммутатор. После этого все сводится к доступу к базовому массиву, что-то вроде этого Matrix[width-x,height-y], который не так уж далек от Matrix[x,y], в моих тестах это была такая же скорость. Еще одним преимуществом является то, что вы можете работать с еще большими вычислениями или изображениями, поскольку вам не нужно сохранять их дважды. Его полностью распараллеливаемый, без перестановки строк / элементов, без копирования памяти. Даже кеширование составляет 2 цикла For с оптимальным значением O (N * N).
Если вас интересуют всего несколько элементов повернутой матрицы, этот код подойдет. Он читабелен, понятен и просто извлекает элементы. Однако при выполнении полного вращения этот код будет медленным. Для каждого элемента у него есть накладные расходы на вызов метода, поиск в 2-м массиве (который имеет умножение), в каждом наборе / получении есть переключатель, кто знает, что он делает для кэширования памяти и т.д. пух и иметь действительно быструю замену элементов петли было бы намного быстрее, чем это. Было бы более читабельным? Возможно нет.
Вот тот, который выполняет вращение на месте вместо использования совершенно нового массива для хранения результата. Я перестал инициализировать массив и распечатать его. Это работает только для квадратных массивов, но они могут быть любого размера. Накладные расходы на память равны размеру одного элемента массива, поэтому вы можете вращать массив любого размера.
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;
}
}
Я вижу как минимум одну ошибку. Если вы собираетесь опубликовать код, протестируйте его или, по крайней мере, скажите, что вы этого не сделали.
Где? Укажи на это, и я исправлю. Я тестировал его, и он отлично работал как с нечетными, так и с четными массивами.
Просто взглянув: второй цикл запускает тесты j <-i-1. Похоже, что j всегда> = 0, а -i-1 всегда отрицательно, поэтому тест никогда не проходит.
Ты прав. Это странно. Я считаю, что там должно быть n, которое я пропустил, когда разместил его. Исправлено в листинге.
это красивое решение. Разум может совершать такие подвиги, если настроен на это. от O (n2) до O (1)
Это не O (1); это все еще O (n ^ 2)
Его O (n ^ 2) с памятью O (1).
Я действительно связываю способ деления массива :)
Ключ к применению этого - «на месте»;
имейте в виду, что это вращение только против часовой стрелки - возможно, вращение по часовой стрелке останется в качестве упражнения для ученика? ;)
Подобное решение упоминается по ссылке ниже geeksforgeeks.org/inplace-rotate-square-matrix-by-90-degrees
@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 ... И если матрица действительно большая, и вы получаете доступ только к паре элементов (редкое использование), это еще более эффективно
Что здесь происходит под одеялом? Это просто псевдоним или память действительно меняется?
@jeffamaphone, память не меняется. Думаю, вы могли бы подумать об этом как о псевдониме, да.
Будет ли это работать, если я вызову GetValue (3,3). Он всегда будет возвращать исходное значение, и транспонирование не происходит?
@Sesh, да, он все еще работает, потому что (A, B) совпадает с (B, A), когда A == B.
Кажется немного несправедливым называть это временным решением за O (1). Чтобы решить проблему, поставленную OP, это все равно займет время O (n ^ 2). Более того, это не решит проблему, потому что возвращает транспонировать. В данном примере транспонирование не используется в качестве решения.
@Goose Bumper - дело в том, что транспонирование не обязательно должно происходить физически. Распечатка матрицы занимает O (n ^ 2), но также и любая операция исчерпывающего чтения, так что от этого никуда не деться. Транспонирование было O (1), и каждое отдельное чтение также O (1), поэтому я придерживаюсь своего утверждения, что это решение имеет постоянное время для вращения и последующих чтений. Как я уже упоминал в ответе, этот метод не всегда самый подходящий.
@ Пол Беттс, не могли бы уточнить? Я не пытаюсь не согласиться, но мне просто интересно узнать о ваших рассуждениях.
@ Пол Беттс, пожалуйста, объясните подробнее. Я не понимаю, почему это не O (1). В любом случае, кто сказал, что я хотел работать на вас? :)
Поскольку для получения полного содержимого нового массива я должен написать функцию: for (i = 1 to n) {for (j = 1 to n) {rotated.GetValue (i, j); }} Вы не волшебным образом сделали это время постоянным, вы просто заставили кого-то написать цикл.
Теперь, если все, что вам нужно, это первые 3 элементы матрицы, это прекрасное решение, но проблема состоит в том, чтобы получить полностью преобразованную матрицу (т.е. предполагая, что вам нужны элементы матрицы все). Вызов этого O (1) - это метод кредитного обмена по умолчанию для анализа алгоритмов - вы не решили проблему, вы просто передали ее кому-то другому :)
@ Пол Беттс: Я понимаю вашу точку зрения, но, как я писал выше в комментариях, даже если у вас действительно есть транспонированная матрица, вам все равно придется написать цикл, если вы хотите прочитать значения. Таким образом, чтение всех значений из матрицы всегда O (N ^ 2) независимо. Разница здесь в том, что если вы снова транспонируете, вращаете, масштабируете, масштабируете и т. д., Вы все равно принимаете удар O (N ^ 2) только один раз. Как я уже сказал, это не всегда лучшее решение, но во многих случаях оно уместно и того стоит. OP, похоже, искал волшебное решение, и оно максимально близко к вам.
Как вы говорите, если вам нужно всего 3 элемента, это быстрее, но я утверждаю, что в большинстве случаев это быстрее в любом случае. Что хорошего в байтах, размещенных в памяти, если вы их не читаете? Независимо от того, будет ли это произвольный или последовательный доступ, их чтение все равно придется заплатить. Это решение просто избавляет вас от необходимости читать их все во время передачи, а затем опять таки позже. Вы читаете только один раз. Если вы забиваете матричный массив для чтения, вам может потребоваться постоянная версия, потому что стоимость отправки виртуальных вызовов может начать перевешивать преимущества этого подхода.
Еще одно соображение заключается в том, является ли исходная матрица неизменной. Этот метод работает только в том случае, если источник не меняется под вами. Есть другой способ думать об этом подходе. Вместо того, чтобы возвращать массив, мы возвращаем обещание предоставить массив, когда (и если) он понадобится. Он похож на IEnumerable. «Расчет» - ленивый.
Мне нравится этот ответ, но я хочу кое-что указать. Распечатка декорированной матрицы (и выполнение других последовательных операций чтения в целом) может быть намного медленнее, чем то же самое с матрицей, которая была повернута в памяти, и это не только из-за вызовов виртуальных методов. Для большой матрицы вы значительно увеличите количество промахов в кэше, читая «вниз», а не «поперек».
@drew noakes, @paul betts: как бы вы это ни объяснили, точка зрения Пола Бетта верна. Вы не поворачивали матрицу, но добавляли косвенные слои, которые будут выполнять преобразование «как раз вовремя». Выполнение чего-либо существенного с этой матрицей (вычисления матриц или манипуляции с изображениями) из-за этого серьезно замедлится.
@ Жаба, ага. Как я уже сказал в посте и в комментариях, все зависит от обстоятельств. Здесь никто не показал это решение, и стоит подумать, даже если вы им не пользуетесь. В некоторых случаях это будет намного лучше, чем вращение массива в памяти.
@MikeDaniels: Это не совсем полная история, потому что, если бы вы просто повернули массив на место "сложным путем" в начале, вам бы тогда пришлось принимать те же самые обращения к кешу. Решение Дрю будет намного медленнее, только если вы выполните операции много с элементами в строковом порядке после их «поворота» (декорирования).
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
Вы можете использовать zip(*reversed(original)) вместо zip(*original[::-1]), чтобы не создавать лишнюю копию исходного списка.
#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++, хотя в большом массиве будут большие накладные расходы на память.
Среди всех этих ответов я нашел и протестировал тот, который компактен и достаточно вращается.
Это лучшая версия на Java: я сделал ее для матрицы с другой шириной и высотой.
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, я вижу, как вы могли бы получить это с помощью примеров индукции / решения. Просто интересно, так ли это было получено или это основано на каком-то математическом принципе, относящемся к Матрицам.
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]);
}
ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я на самом деле не тестировал это. Давай поиграем в жучок!
Разве это не просто транспонирование матрицы, а не ее поворот?
Рубиновый путь: .transpose.map &:reverse
Это еще проще: array.reverse.transpose вращает массив по часовой стрелке, а array.transpose.reverse вращает его против часовой стрелки. map не нужен.
Время O (n ^ 2) и алгоритм пространства O (1) (без каких-либо обходных путей и непонятных вещей!)
Повернуть на +90:
Повернуть на -90:
Способ 1:
Способ 2:
Повернуть на +180:
Способ 1: дважды повернуть на +90
Способ 2: перевернуть каждую строку, а затем перевернуть каждый столбец (транспонировать)
Повернуть на -180:
Способ 1: повернуть на -90 дважды
Способ 2: перевернуть каждый столбец, а затем перевернуть каждую строку
Способ 3: повернуть на +180, так как они такие же
Это было очень полезно для меня; Я смог написать алгоритм, когда узнал «версию [псевдокода» этой операции. Спасибо!
Один из моих любимых SO-ответов всех времен. Очень поучительно!
Вот реализация JavaScript JSFiddle, если кому-то интересно.
Повернуть на -90: (1) Поменять местами каждую строку; (2) Транспонировать. Haskell: rotateCW = map reverse . transpose и rotateCCW = transpose . map reverse
Чтобы повернуть на -90, реверсирование каждого ряд и затем транспонирование будет быстрее, чем транспонирование с последующим обращением каждого столбец. Согласованность кеша - большое дело
Пространство не было бы O (1) для простого решения «Транспонирование матрицы на месте» не так просто. Для простого решения вы должны использовать временную матрицу для хранения транспонированной матрицы. Тем не менее, хорошее решение!
Красивый. Следует отметить, что пространство O (1) невозможно, если размеры разные, независимо от того, какой другой массив должен быть создан.
В чем разница между поворотом на 180 и -180?
@ElgsQianChen Нет. 90, 180, 270 соответствуют -270, -180, -90 в другом направлении соответственно. Следовательно, поворот на 90 совпадает с поворотом на -270 и так далее. Фактически, для всех случаев требуется только три уникальных поворота (если 0, можно просто вернуть исходную матрицу).
Блестящее решение. Это имеет некоторое сходство с вращением одномерного массива - что, IMHO, является его яркостью. В Интернете есть и другие решения, и это лучшее.
Я единственный, кто не может доказать правильность этого алгоритма? :(
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):
поверните самые внешние данные, тогда вы можете получить следующий результат:
[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, затем вы можете вращать самые внешние элементы.
Поэтому мы можем заключить это следующим образом:
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;
}
На каком это языке?
вот моя реализация на месте в 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 Этот ответ подробно объясняет это.
Невозможно сделать это быстрее, чем O (n ^ 2) для вращения на месте, по той причине, что если мы хотим повернуть матрицу, мы должны коснуться всего элемента n ^ 2 хотя бы один раз, независимо от того, какой алгоритм вы реализуете.
В этом случае п должно относиться к количеству элементов в матрице, а не к длине одной из ее сторон, что делает поворот матрицы O (n) (т.е. линейным)
Время - 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).
@JasonOster Я считаю, что это пространство O (1), поскольку оно не требует дополнительного места.
@ffledgling Моя ошибка. O (1) космическая сложность, да. O (n) временная сложность.
Космическая сложность тоже O (n). Сложность пространства должна включать пространство размера входной переменной. careercup.com/question?id=14952322
Как я могу изменить это, чтобы оно работало против часовой стрелки?
Вот рекурсивный способ 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]), который выполняет то же самое, когда все строки имеют одинаковую длину, без попытки обработать функцию преобразования.
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);Этот код предполагает наличие массива вложенных массивов, где каждый внутренний массив представляет собой строку.
Метод позволяет вам читать (или записывать) элементы (даже в случайном порядке), как если бы массив был повернут или преобразован. Теперь просто выберите нужную функцию для вызова, возможно, по ссылке, и вперед!
Концепция может быть расширена для применения преобразований аддитивно (и неразрушающим образом) с помощью методов доступа. Включая произвольные угловые повороты и масштабирование.
Однако на самом деле ни один из них не вращался из исходного массива. Первый, конечный результат просто транспонируется. Во втором случае вы, кажется, только что перемешали строки или отразили их по центру по горизонтали. Третий, вы только перевернули строки, а четвертый также транспонирован. Ни один из них фактически не был «повернут».
В последних двух примерах есть некоторые ошибки. Исправить несложно. Я прямо указал, что это решение нет - вращение на месте. Это функция преобразования, что делает ее пригодной для ленивых итераций.
За исключением того, что ротации нет, поэтому вы фактически не ответили на то, что спросил OP.
@ SM177Y Другой редактор добавил к моему ответу нерабочий пример кода. Я вижу, как это вас смутило. Исправлены ошибки в итерационных циклах. Предоставленные функции фактически "вращают" данные в массивах.
Также важной деталью является то, что пример кода действительно размывает исходный ответ, который я предоставил, который пытался проиллюстрировать мощь функциональных преобразований над решениями линейной пространственно-временной сложности. С функциональным преобразованием вы уже выполняет итерацию или иным образом обращается к элементам массива, поэтому преобразование считается «бесплатным» в смысле постоянной пространственной и временной сложности.
Решение 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);
}
}
Решение не на месте.
Вы можете сделать это в 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
*/
Я тебя не понял?
(Сравните версии. Не используйте пробелы и запятые / операторы или не используйте примитив swap.)
Вот статический универсальный метод 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)
`
#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-массива.
В Эйген (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]);
}
}
Мне нравится это решение, потому что оно довольно интуитивно понятное и простое, спасибо
Как вы могли уйти с меньшим, чем n ^ 2? Все элементы должны быть прочитаны и установлены, а элементов n ^ 2