Як ви обертаєте двовимірний масив?


302

Натхненний публікацією Реймонда Чена , скажімо, у вас є двовимірний масив 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? Що робити, якщо матриця була 10000x10000?


99
Як ви могли, можливо, піти з менш ніж n ^ 2? Усі елементи повинні бути прочитані та встановлені, а є n ^ 2 елементи
erikkallen


9
Який твій росіянин? Ви не кажете, якщо двовимірний масив є квадратним (це не в загальному випадку! Наприклад, вектор - це матриця з одним виміром 1), але, здається, ви розумієте, що n - ширина і висота, і, отже, n² елементів . Було б більше сенсу мати n кількість елементів, при яких n = w × h.
niXar

1
Ось швидкий спосіб зробити це: зберігати індекси рядків та стовпців (скажімо, i та j). Транспонування займає постійний час (просто поміняйте індекси :). Ви можете зробити те ж саме з обертаннями (грайте з індексами).
saadtaame

4
У випадку, коли n ^ 2 неможливо. Ви можете створити інтерфейс, який отримує доступ до кожного елемента. Потім дано (i, j), застосуйте поворот до (i, j) доступу до обертового елемента і поверніться. Може бути не найкращим солом, але працює.
Переплутайте

Відповіді:


140

Ось це в 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;
}

6
Звичайно, а як щодо рішення, що використовує O (1) пам'ять?
AlexeyMK

20
Ваше рішення має складність простору O (n ^ 2). Треба робити краще
Kshitij Jain

6
Як щодо матриці NXM?
Рохіт

17
Складність лінійна за кількістю елементів у масиві. Якщо N - кількість елементів, складність дорівнює O (N). Якщо N - довжина сторони, то так, складність дорівнює O (N ^ 2), але це все ж оптимально. Ви повинні прочитати кожен елемент хоча б один раз. Друк матриці той самий складний
Алехандро

6
Для обертання на -90 градусів:ret[i][j] = matrix[j][n - i - 1]
Duncan Luk

387

O (n ^ 2) час та алгоритм простору O (1) (без жодних обхідних шляхів та химерних речей!)

Поворот на +90:

  1. Перенести
  2. Переверніть кожен ряд

Поворот на -90:

Спосіб 1:

  1. Перенести
  2. Переверніть кожен стовпець

Спосіб 2:

  1. Переверніть кожен ряд
  2. Перенести

Поворот на +180:

Спосіб 1 : Поверніть на +90 двічі

Спосіб 2 : Переверніть кожен рядок, а потім переверніть кожен стовпець (Transpose)

Поворот на -180:

Спосіб 1 : Поверніть на -90 двічі

Спосіб 2 : Переверніть кожен стовпець, а потім поверніть кожен рядок

Спосіб 3 : Поверніть на +180, оскільки вони однакові


4
Це було дуже корисно для мене; Мені вдалося написати алгоритм, коли я знав "[псевдо-] версію коду" цієї операції. Дякую!
дума

13
Один з моїх улюблених відповідей ТА усіх часів. Дуже повчально!
g33kz0r

2
Ось реалізація JavaScript JSFiddle, якщо хтось зацікавлений.
Містер Polywhirl

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

5
Яка різниця між обертовими на 180 і -180?
Qian Chen

177

Я хотів би додати трохи детальніше. У цій відповіді ключові поняття повторюються, темп повільний і навмисно повторюваний. Надане тут рішення не є найбільш синтаксично компактним, проте воно призначене для тих, хто бажає дізнатися, що таке обертання матриці та що реалізується в результаті.

По-перше, що таке матриця? Для цілей цієї відповіді матриця - це просто сітка, де ширина і висота однакові. Зауважимо, ширина та висота матриці можуть бути різними, але для простоти в цьому підручнику розглядаються лише матриці з однаковою шириною та висотою ( квадратні матриці ). І так, матриці - це множина матриці.

Прикладними матрицями є: 2 × 2, 3 × 3 або 5 × 5. Або, загальніше, N × N. Матриця 2 × 2 матиме 4 квадрати, оскільки 2 × 2 = 4. Матриця 5 × 5 матиме 25 квадратів, оскільки 5 × 5 = 25. Кожен квадрат називається елементом або записом. Ми зобразимо кожен елемент із періодом ( .) на діаграмах нижче:

2 × 2 матриці

. .
. .

3 × 3 матриця

. . .
. . .
. . .

4 × 4 матриця

. . . .
. . . .
. . . .
. . . .

Отже, що означає обертати матрицю? Візьмемо матрицю 2 × 2 і покладемо кілька чисел у кожен елемент, щоб можна було спостерігати обертання:

0 1
2 3

Поворот цього на 90 градусів дає нам:

2 0
3 1

Ми буквально повернули всю матрицю праворуч так само, як повернути кермо автомобіля. Це може допомогти подумати про "перекидання" матриці на її праву сторону. Ми хочемо написати функцію в Python, яка приймає матрицю і обертається один раз праворуч. Підпис функції буде:

def rotate(matrix):
    # Algorithm goes here.

Матриця буде визначена за допомогою двовимірного масиву:

matrix = [
    [0,1],
    [2,3]
]

Тому перша позиція індексу має доступ до рядка. Друга позиція індексу отримує доступ до стовпця:

matrix[row][column]

Ми визначимо функцію утиліти для друку матриці.

def print_matrix(matrix):
    for row in matrix:
        print row

Один із способів обертання матриці - це робити її пошарово. Але що таке шар? Подумайте про цибулю. Так само, як шари цибулі, коли кожен шар видаляється, ми рухаємося до центру. Інші аналогії - лялька Матрьошка або гра-передача.

Ширина та висота матриці диктують кількість шарів у цій матриці. Давайте використовувати різні символи для кожного шару:

Матриця 2 × 2 має 1 шар

. .
. .

Матриця 3 × 3 має 2 шари

. . .
. x .
. . .

Матриця 4 × 4 має 2 шари

. . . .
. x x .
. x x .
. . . .

Матриця 5 × 5 має 3 шари

. . . . .
. x x x .
. x O x .
. x x x .
. . . . .

Матриця 6 × 6 має 3 шари

. . . . . .
. x x x x .
. x O O x .
. x O O x .
. x x x x .
. . . . . .

Матриця 7 × 7 має 4 шари

. . . . . . .
. x x x x x .
. x O O O x .
. x O - O x .
. x O O O x .
. x x x x x .
. . . . . . .

Ви можете помітити, що збільшення ширини та висоти матриці на одиницю не завжди збільшує кількість шарів. Беручи вищезазначені матриці та табулюючи шари та розміри, ми бачимо, що кількість шарів збільшується один раз на кожні два кроки ширини та висоти:

+-----+--------+
| N×N | Layers |
+-----+--------+
| 1×1 |      1 |
| 2×2 |      1 |
| 3×3 |      2 |
| 4×4 |      2 |
| 5×5 |      3 |
| 6×6 |      3 |
| 7×7 |      4 |
+-----+--------+

Однак не всі шари потребують обертання. Матриця 1 × 1 однакова до і після обертання. Центральний шар 1 × 1 завжди однаковий до і після обертання незалежно від того, наскільки велика загальна матриця:

+-----+--------+------------------+
| N×N | Layers | Rotatable Layers |
+-----+--------+------------------+
| 1×1 |      1 |                0 |
| 2×2 |      1 |                1 |
| 3×3 |      2 |                1 |
| 4×4 |      2 |                2 |
| 5×5 |      3 |                2 |
| 6×6 |      3 |                3 |
| 7×7 |      4 |                3 |
+-----+--------+------------------+

З огляду на матрицю N × N, як ми можемо програмно визначити кількість шарів, які нам потрібно обертати? Якщо розділити ширину або висоту на два і ігнорувати решту, ми отримаємо наступні результати.

+-----+--------+------------------+---------+
| N×N | Layers | Rotatable Layers |   N/2   |
+-----+--------+------------------+---------+
| 1×1 |      1 |                0 | 1/2 = 0 |
| 2×2 |      1 |                1 | 2/2 = 1 |
| 3×3 |      2 |                1 | 3/2 = 1 |
| 4×4 |      2 |                2 | 4/2 = 2 |
| 5×5 |      3 |                2 | 5/2 = 2 |
| 6×6 |      3 |                3 | 6/2 = 3 |
| 7×7 |      4 |                3 | 7/2 = 3 |
+-----+--------+------------------+---------+

Зауважте, як N/2відповідає кількість шарів, які потрібно обертати? Іноді кількість обертових шарів на одиницю менше загальної кількості шарів у матриці. Це відбувається, коли внутрішній шар складається лише з одного елемента (тобто матриці 1 × 1), і тому його не потрібно обертати. Він просто ігнорується.

Нам, безсумнівно, потрібна ця інформація в нашій функції для обертання матриці, тому давайте додамо її зараз:

def rotate(matrix):
    size = len(matrix)
    # Rotatable layers only.
    layer_count = size / 2

Тепер ми знаємо, що таке шари і як визначити кількість шарів, які насправді потребують обертання, як ми виділяємо один шар, щоб ми могли його обертати? По-перше, ми оглядаємо матрицю від самого зовнішнього шару, всередину, до самого внутрішнього шару. Матриця 5 × 5 має три шари загалом і два шари, які потребують обертання:

. . . . .
. x x x .
. x O x .
. x x x .
. . . . .

Розглянемо спочатку стовпці. Положення стовпців, що визначають самий зовнішній шар, якщо вважати, що ми рахуємо з 0, дорівнюють 0 і 4:

+--------+-----------+
| Column | 0 1 2 3 4 |
+--------+-----------+
|        | . . . . . |
|        | . x x x . |
|        | . x O x . |
|        | . x x x . |
|        | . . . . . |
+--------+-----------+

0 і 4 - також позиції рядків для самого зовнішнього шару.

+-----+-----------+
| Row |           |
+-----+-----------+
|   0 | . . . . . |
|   1 | . x x x . |
|   2 | . x O x . |
|   3 | . x x x . |
|   4 | . . . . . |
+-----+-----------+

Це завжди буде так, оскільки ширина і висота однакові. Тому ми можемо визначити позиції стовпців та рядків шару лише з двома значеннями (а не з чотирма).

Просуваючись всередину до другого шару, положення стовпців дорівнює 1 і 3. І, так, ви здогадалися, це так само для рядків. Важливо розуміти, що нам потрібно було збільшувати та зменшувати позиції рядків та стовпців при переході всередину до наступного шару.

+-----------+---------+---------+---------+
|   Layer   |  Rows   | Columns | Rotate? |
+-----------+---------+---------+---------+
| Outermost | 0 and 4 | 0 and 4 | Yes     |
| Inner     | 1 and 3 | 1 and 3 | Yes     |
| Innermost | 2       | 2       | No      |
+-----------+---------+---------+---------+

Отже, для огляду кожного шару нам потрібна петля з лічильниками, що збільшуються і зменшуються, що представляють собою переміщення всередину, починаючи з самого зовнішнього шару. Ми назвемо це нашим "циклом шару".

def rotate(matrix):
    size = len(matrix)
    layer_count = size / 2

    for layer in range(0, layer_count):
        first = layer
        last = size - first - 1
        print 'Layer %d: first: %d, last: %d' % (layer, first, last)

# 5x5 matrix
matrix = [
    [ 0, 1, 2, 3, 4],
    [ 5, 6, 6, 8, 9],
    [10,11,12,13,14],
    [15,16,17,18,19],
    [20,21,22,23,24]
]

rotate(matrix)

Код, що знаходиться вище, проходить через (рядок і стовпець) позиції будь-яких шарів, які потребують обертання.

Layer 0: first: 0, last: 4
Layer 1: first: 1, last: 3

Тепер у нас є петля, що забезпечує положення рядків і стовпців кожного шару. Змінні firstта lastідентифікують позицію індексу першого та останнього рядків та стовпців. Посилання на наші таблиці та рядки та стовпці:

+--------+-----------+
| Column | 0 1 2 3 4 |
+--------+-----------+
|        | . . . . . |
|        | . x x x . |
|        | . x O x . |
|        | . x x x . |
|        | . . . . . |
+--------+-----------+

+-----+-----------+
| Row |           |
+-----+-----------+
|   0 | . . . . . |
|   1 | . x x x . |
|   2 | . x O x . |
|   3 | . x x x . |
|   4 | . . . . . |
+-----+-----------+

Таким чином ми можемо орієнтуватися по шарах матриці. Тепер нам потрібен спосіб навігації всередині шару, щоб ми могли переміщувати елементи навколо цього шару. Зауважте, елементи ніколи не «перескакують» з одного шару на інший, але вони рухаються всередині відповідних шарів.

Обертання кожного елемента в шарі обертає весь шар. Обертання всіх шарів у матриці обертає всю матрицю. Це речення дуже важливе, тому, будь ласка, постарайтеся, щоб зрозуміти це, перш ніж продовжувати.

Тепер нам потрібен спосіб фактично переміщення елементів, тобто обертання кожного елемента, а згодом і шару, і в кінцевому рахунку матриці. Для простоти ми повернемося до матриці 3x3, яка має один обертовий шар.

0 1 2
3 4 5
6 7 8

Наш цикл шарів містить індекси першого та останнього стовпців, а також першого та останнього рядків:

+-----+-------+
| Col | 0 1 2 |
+-----+-------+
|     | 0 1 2 |
|     | 3 4 5 |
|     | 6 7 8 |
+-----+-------+

+-----+-------+
| Row |       |
+-----+-------+
|   0 | 0 1 2 |
|   1 | 3 4 5 |
|   2 | 6 7 8 |
+-----+-------+

Оскільки наші матриці завжди квадратні, нам потрібно всього дві змінні, firstі last, оскільки позиції індексу однакові для рядків і стовпців.

def rotate(matrix):
    size = len(matrix)
    layer_count = size / 2

    # Our layer loop i=0, i=1, i=2
    for layer in range(0, layer_count):

        first = layer
        last = size - first - 1

        # We want to move within a layer here.

Перші та останні змінні можуть бути легко використані для посилання на чотири кути матриці. Це тому, що самі кути можна визначити, використовуючи різні перестановки firstта last(без віднімання, додавання чи зміщення цих змінних):

+---------------+-------------------+-------------+
| Corner        | Position          | 3x3 Values  |
+---------------+-------------------+-------------+
| top left      | (first, first)    | (0,0)       |
| top right     | (first, last)     | (0,2)       |
| bottom right  | (last, last)      | (2,2)       |
| bottom left   | (last, first)     | (2,0)       |
+---------------+-------------------+-------------+

З цієї причини ми починаємо обертання у зовнішніх чотирьох кутах - ми повернемо їх першими. Давайте виділимо їх *.

* 1 *
3 4 5
* 7 *

Ми хочемо поміняти місцями кожного *з *праворуч від нього. Тож давайте продовжимо роздрукувати наші кути, визначені, використовуючи лише різні перестановки firstта last:

def rotate(matrix):
    size = len(matrix)
    layer_count = size / 2
    for layer in range(0, layer_count):

        first = layer
        last = size - first - 1

        top_left = (first, first)
        top_right = (first, last)
        bottom_right = (last, last)
        bottom_left = (last, first)

        print 'top_left: %s' % (top_left)
        print 'top_right: %s' % (top_right)
        print 'bottom_right: %s' % (bottom_right)
        print 'bottom_left: %s' % (bottom_left)

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

rotate(matrix)

Вихід повинен бути:

top_left: (0, 0)
top_right: (0, 2)
bottom_right: (2, 2)
bottom_left: (2, 0)

Тепер ми могли легко поміняти місцями кожен кут зсередини нашого шару шару:

def rotate(matrix):
    size = len(matrix)
    layer_count = size / 2
    for layer in range(0, layer_count):

        first = layer
        last = size - first - 1

        top_left = matrix[first][first]
        top_right = matrix[first][last]
        bottom_right = matrix[last][last]
        bottom_left = matrix[last][first]

        # bottom_left -> top_left
        matrix[first][first] = bottom_left
        # top_left -> top_right
        matrix[first][last] = top_left
        # top_right -> bottom_right
        matrix[last][last] = top_right
        # bottom_right -> bottom_left
        matrix[last][first] = bottom_right


print_matrix(matrix)
print '---------'
rotate(matrix)
print_matrix(matrix)

Матриця перед обертаннями кутів:

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

Матриця після обертових кутів:

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

Чудово! Ми успішно обертали кожен кут матриці. Але ми не обертаємо елементи посередині кожного шару. Очевидно, що нам потрібен спосіб ітерації всередині шару.

Проблема полягає в тому, що єдиний цикл у нашій функції досі (наш цикл шарів) переходить до наступного шару на кожній ітерації. Оскільки наша матриця має лише один обертовий шар, петля шару виходить після обертання лише кутів. Давайте розглянемо, що відбувається з більшою матрицею 5 × 5 (де два шари потрібно обертати). Код функції пропущено, але він залишається таким же, як і вище:

matrix = [
[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]
]
print_matrix(matrix)
print '--------------------'
rotate(matrix)
print_matrix(matrix)

Вихід:

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

Це не повинно дивувати, що кути самого зовнішнього шару повернуті, але, можливо, ви також помітите, що кути наступного шару (всередину) також повернуті. Це має сенс. Ми написали код для навігації по шарах, а також для обертання кутів кожного шару. Це відчувається як прогрес, але, на жаль, ми повинні зробити крок назад. Просто переходити на наступний шар просто не буде, поки попередній (зовнішній) шар повністю не повернеться. Тобто, поки кожен елемент у шарі не повернеться. Обертання лише кутів не обійдеться!

Зробити глибокий подих. Нам потрібна ще одна петля. Вкладена петля не менше. Новий, вкладений цикл, буде використовувати firstі lastзмінні, плюс зсув , щоб переміщатися в межах шару. Ми будемо називати цей новий цикл нашим "циклом елементів". Цикл елементів буде відвідувати кожен елемент уздовж верхнього ряду, кожен елемент праворуч, кожен елемент уздовж нижнього ряду та кожен елемент вгору ліворуч.

  • Для просування вперед по верхньому рядку потрібно збільшувати індекс стовпців.
  • Якщо рухатись правою стороною вниз, потрібно збільшувати індекс рядків.
  • Для переміщення назад по нижній частині потрібно зменшити індекс стовпця.
  • Для руху лівою стороною потрібно зменшити індекс рядка.

Це звучить складно, але це легко, тому що кількість разів, де ми збільшуємо та зменшуємо досягнення вищезгаданого, залишається однаковим по всіх чотирьох сторонах матриці. Наприклад:

  • Перемістіть 1 елемент у верхньому ряду.
  • Перемістіть 1 елемент вправо.
  • Перемістіть 1 елемент назад по нижньому ряду.
  • Перемістіть 1 елемент вгору лівою стороною.

Це означає, що ми можемо використовувати одну змінну в поєднанні зі змінними firstта lastдля переміщення всередині шару. Це може допомогти зауважити, що переміщення по верхньому ряду та вниз правою стороною вимагає збільшення. Під час руху назад по нижній та лівій стороні вгору потрібно обидві декременти.

def rotate(matrix):
    size = len(matrix)
    layer_count = size / 2

    # Move through layers (i.e. layer loop).
    for layer in range(0, layer_count):

            first = layer
            last = size - first - 1

            # Move within a single layer (i.e. element loop).
            for element in range(first, last):

                offset = element - first

                # 'element' increments column (across right)
                top_element = (first, element)
                # 'element' increments row (move down)
                right_side = (element, last)
                # 'last-offset' decrements column (across left)
                bottom = (last, last-offset)
                # 'last-offset' decrements row (move up)
                left_side = (last-offset, first)

                print 'top: %s' % (top)
                print 'right_side: %s' % (right_side)
                print 'bottom: %s' % (bottom)
                print 'left_side: %s' % (left_side)

Тепер нам просто потрібно присвоїти верхню частину праворуч, праву частину донизу, нижню - ліву сторону, а ліву частину - верхню. Збираючи це все разом, ми отримуємо:

def rotate(matrix):
    size = len(matrix)
    layer_count = size / 2

    for layer in range(0, layer_count):
        first = layer
        last = size - first - 1

        for element in range(first, last):
            offset = element - first

            top = matrix[first][element]
            right_side = matrix[element][last]
            bottom = matrix[last][last-offset]
            left_side = matrix[last-offset][first]

            matrix[first][element] = left_side
            matrix[element][last] = top
            matrix[last][last-offset] = right_side
            matrix[last-offset][first] = bottom

Дано матрицю:

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

Наша rotateфункція призводить до:

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

Спочатку я відчував себе "вау, найкраще пояснення коли-небудь", але, прочитавши його пару разів (щоб переконатися, що я не пропустив нічого важливого в морі слів), моя думка змінилася на "людина, я розумію, може ми продовжуємо його рухатись, будь ласка? " Досі не вимагає того, щоб зайняти години, щоб скласти таку детальну відповідь.
Abhijit Sarkar

1
@AbhijitSarkar - Дякую за голосування, і я сподіваюся, що це принаймні допомогло невеликим чином. Звичайно, ти маєш рацію, моя відповідь багатослівна. Однак це було навмисно на відміну від переважної більшості відповідей. Як я сказав на самому початку своєї відповіді: "У цій відповіді ключові поняття повторюються, темп повільний і навмисно повторюваний". Якщо у вас є зміни, які зберігають чіткість і необхідність повторюваності, але зменшують кількість слів, я дуже відкритий для пропозицій. Або просто відредагуйте :)
Джек

@jack Дійсно добре пояснення. Однак я не міг зрозуміти, як ви придумали зміщення = елемент - перший і останній = розмір - перший - 1? Важко зрозуміти це? Крім того, чи є останнім зміщенням таке ж, як зміщення?
ашишмешрам

1
TL; DR:list(zip(*reversed(your_list_of_lists)))
Борис

127

Пітон:

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

і проти годинникової стрілки:

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

Як це працює:

zip(*original)буде поміняти осі 2d масивів шляхом складання відповідних елементів зі списків у нові списки. ( *Оператор повідомляє функцію розподіляти містяться списки в аргументи)

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

В [::-1]елементах заяви реверсу масиву (див Extended шматочки або це питання ):

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

Нарешті, поєднання двох призведе до перетворення обертання.

Зміна розміщення [::-1]виворотних списків на різних рівнях матриці.


3
Я вважаю, що цей код походить від Пітера Норвіга: norvig.com/python-iaq.html
Йосип

Ви можете використовувати zip(*reversed(original))замість цього, zip(*original[::-1])щоб уникнути створення додаткової копії оригінального списку.
Борис

70

Ось такий, який виконує обертання на місці замість використання абсолютно нового масиву для утримання результату. Я припинив ініціалізацію масиву і роздрукував його. Це працює лише для квадратних масивів, але вони можуть бути будь-яких розмірів. Накладні витрати на пам'ять дорівнюють розміру одного елемента масиву, тому ви можете робити обертання настільки великого масиву, скільки вам потрібно.

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

Я можу побачити хоча б одну помилку. Якщо ви збираєтесь розмістити код, протестуйте його або, принаймні, скажіть, що ви цього не зробили.
Х'ю Аллен

1
Де? Вкажіть це, і я це виправлю. Я тестував це, і він працював чудово як на непарних, так і на парних масивах.
dagorym

2
це прекрасне рішення. Розум може виконувати такі подвиги, якщо ставиться за призначенням. від O (n2) до O (1)
MoveFast

2
Це не О (1); це все ще O (n ^ 2)
дума

11
Його O (n ^ 2) з пам'яттю O (1).
Ніл

38

Тут є багато хорошого коду, але я просто хочу показати, що відбувається геометрично, щоб ви могли трохи краще зрозуміти логіку коду. Ось як я би підходив до цього.

Перш за все, не плутайте це з переміщенням, що дуже легко.

ідея basica полягає в тому, щоб ставитися до неї як до шарів, і ми обертаємо один шар за один раз.

скажімо, у нас є 4x4

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

а потім 2-й косий алмаз

        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

Я вважаю, що це те саме рішення, що і при розгортці інтерв'ю з кодуванням. Але мені подобається покрокове пояснення. Дуже приємно і ретельно.
Нафстор

@PDN Ця відповідь детально пояснює це.
Mathias Bynens

35

Як я вже говорив у своєму попередньому дописі, ось такий код у 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;
}

Гаразд, я піднесу руку, вона фактично не змінює оригінальний масив при обертанні. Але в системі OO, яка не має значення, поки об'єкт виглядає так, що він повертається до клієнтів класу. На даний момент клас Matrix використовує посилання на вихідні дані масиву, тому зміна будь-якого значення m1 також змінить m2 і m3. Невелика зміна в конструкторі для створення нового масиву та копіювання значень до нього впорядкує це.


4
Браво! Це дуже приємне рішення, і я не знаю, чому це не прийнята відповідь.
мартинатім

@martinatime: можливо, тому що вона в 5 разів більша
Жаба

@Toad: Ну, написання коду - це завжди компроміс між конкуруючими вимогами: швидкістю, розміром, вартістю тощо
Skizz

15
правда ... ще одна проблема полягає в тому, що матриця насправді не обертається, а повертається "просто в часі". Що чудово підходить для доступу до декількох елементів, але було б жахливо, якби ця матриця була використана в обчисленнях або маніпуляціях із зображенням. Так що сказати O (1) насправді не справедливо.
Жаба

23

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

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
+1 ... І якщо матриця дійсно велика і ви отримуєте доступ лише до декількох елементів (рідке використання), це ще ефективніше
lothar

16
Здається, це несправедливо називати це рішенням часу (O). Для вирішення поставленої ОП задачі все одно знадобиться час O (n ^ 2). Мало того, це не вирішило б проблему, тому що вона повертає транспозит . Наведений приклад не має транспонінгу як рішення.
Влад Імпала

5
Тепер, якщо все, що ви хотіли, були перші 3 елементи матриці, це прекрасне рішення, але проблема полягає в тому, щоб отримати повністю перетворену матрицю (тобто припускаючи, що вам потрібні всі елементи матриці). Виклик цього O (1) - це метод заміни кредитного дефолту аналізу алгоритму - ви не вирішили проблему, ви просто натиснули її на когось іншого :)
Ana Betts

4
@Paul Betts: Я розумію, але, як я писав вище в коментарях, навіть якщо ви переклали матрицю, вам все одно потрібно записати цикл, якщо ви хочете прочитати значення. Тому читання всіх значень з матриці завжди є O (N ^ 2) незалежно. Різниця тут полягає в тому, що якщо ви переміщуєте, обертаєте, масштабуєте, знову масштабуєте і т. Д., Ви все одно займетеся ударом O (N ^ 2) один раз. Як я вже говорив, це не завжди найкраще рішення, але в багатьох випадках воно є доцільним і вартим. ОП здавалося, що шукає магічне рішення, і це настільки ж близько, як ви отримаєте.
Дрю Ноакс

9
Мені подобається ця відповідь, але я хочу щось зазначити. Друк оформленої матриці (і взагалі виконання інших послідовних читань) може бути набагато повільніше, ніж робити те ж саме з матрицею, яка повертається в пам'яті, і це не лише через виклики віртуальних методів. Що стосується великої матриці, ви збільшите значно збільшити кількість пропусків кешу, отриманих, читаючи "вниз", а не "поперек".
Майк Даніельс

18

Це краща його версія на 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 відповідь, я бачу, як ви могли це отримати завдяки індукції / вирішенню прикладів. Цікаво, чи так це було отримано чи це засноване на якомусь математичному принципі, що стосується матриць.
Квест Монгер

17

Рубіновий шлях: .transpose.map &:reverse


1
Це навіть простіше, ніж це: array.reverse.transposeобертає масив за годинниковою стрілкою, а array.transpose.reverseобертає його проти годинникової стрілки. У цьому немає потреби map.
Giorgi Gzirishvili

13

Відповідей уже багато, і я знайшов дві, які стверджують, що 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.log(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.log(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.log(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.log(newarr);

Цей код передбачає масив вкладених масивів, де кожен внутрішній масив є рядком.

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

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


Жодне з них насправді не обертається від вихідного масиву. Перший, кінцевий результат просто переноситься. У другому, ви, здається, щойно перетасували рядки або дзеркально проходили по горизонтальному центру. Третій, ви лише перевернули рядки, а четвертий також перемістили. Жоден з яких насправді не був «поворотним».
SM177Y

У двох останніх прикладах є деякі помилки. Тривіально виправити. Я чітко зазначив, що це рішення не є обертанням на місці. Це функція перетворення, яка робить її придатною для ледачої ітерації.
Джейсон Остер

За винятком того, що немає ротації, тому ви насправді не відповіли на те, що запитувала ОП.
SM177Y

@ SM177Y Інший редактор додав до моєї відповіді непрацюючий приклад коду. Я бачу, як вас це збентежило. Я виправив помилки в ітераційних петлях. Надані функції насправді "обертають" дані в масивах.
Джейсон Остер

Також важливою деталлю є те, що приклад коду справді вимиває оригінальну відповідь, яку я надавав, що намагалася проілюструвати силу функціональних перетворень над лінійними рішеннями складності простору та часу. За допомогою функціональної трансформації ви вже повторюєте або іншим чином отримуєте доступ до елементів масиву , тому перетворення вважається "вільним" у розумінні постійної просторової та часової складності.
Джейсон Остер

10

Кілька людей вже склали приклади, які стосуються створення нового масиву.

Ще кілька речей, які слід врахувати:

(а) Замість того, щоб насправді переміщувати дані, просто перейдіть поворотний масив по-іншому.

(b) Зробити обертання на місці може бути трохи складніше. Вам знадобиться трохи місця подряпин (можливо, приблизно дорівнює одному розміру рядка або стовпця). Існує стародавній документ ACM про те, як робити місцеві транспондери ( http://doi.acm.org/10.1145/355719.355729 ), але їх прикладним кодом є гадкий FORTRAN, накладений на готи.

Додаток:

http://doi.acm.org/10.1145/355611.355612 - це ще один, нібито вищий, алгоритм транспозиції на місці.


Я згоден з цим. Створіть метод, який визначає переклад між вихідними даними та "повернутими" даними.
мартинатім

8

Відповідь Ніка працювала б і для масиву 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) від лівого верхнього кута до верхнього правого кута. Ви просто переносите з одного на інший.


6

Час - 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;
        }
    }
}

Це не О (1). Це O (n).
Джейсон Остер

@JasonOster Я вважаю, що це O (1) простір, оскільки він не займає додаткового місця.
ffledgling

@ffledgling Моя помилка. O (1) складність простору, так. O (n) часова складність.
Джейсон Остер

Складність простору також є O (n). Складність простору повинна містити простір розміру вхідної змінної. carecup.com/question?id=14952322
Джейсон Хе

Як я можу змінити це для роботи для обертання проти годинникової стрілки?
MD XF

5

Ось моя версія 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

4

ось метод обертання в просторі, 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;
}

3

Реалізація псевдокоду dimple +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;
}

3

Зробити це можна за допомогою 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

Вихідний код Java для цього:

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 

2

Це моя реалізація в С, 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;
        }
    }
}

2

Ось версія 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;
        }
    }
}

Спочатку спочатку обертають найповніший шар, а потім переміщуються до внутрішнього шару.


2

З лінійної точки зору розглянемо матриці:

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

Тепер візьміть A транспонінг

     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

Це розширюється для будь-якої матриці nxn. І швидко застосувати цю концепцію в коді:

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

2

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

2

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]
]

1

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

X - розмір масиву, на якому зображена графіка.


1

#transpose - це стандартний метод класу Ruby's Array, таким чином:

% 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), але лише для спеціально побудованих матриць (таких як розріджені матриці)


1

Код С для обертання матриці на 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;
        }
    }
}

1

ось моя реалізація In Place на 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;
        }
    }
}

1

Ось моя спроба обертання матриці на 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;
}

1

@dagorym: Так, чоловіче. Я звикав до цього, як гарна головоломка "Мені нудно, що я можу подумати". Я придумав свій місцевий код транспозиції, але потрапив сюди, щоб знайти твою майже ідентичну моїй ... ах, ну. Ось вона в Рубі.

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

1
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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.