¿Cómo se rota una matriz bidimensional?

302

Inspirado por la publicación de Raymond Chen , digamos que tiene una matriz bidimensional 4x4, escriba una función que la rote 90 grados. Raymond se vincula a una solución en pseudocódigo, pero me gustaría ver algunas cosas del mundo real.

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

Se convierte en:

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

Actualización : la respuesta de Nick es la más sencilla, pero ¿hay alguna manera de hacerlo mejor que n ^ 2? ¿Qué pasa si la matriz era 10000x10000?

swilliams
fuente
99
¿Cómo podrías salirte con menos de n ^ 2? Todos los elementos deben leerse y establecerse, y hay n ^ 2 elementos
erikkallen
99
¿Cuál es su n? No dice si la matriz 2D es cuadrada (¡no es en el caso general! Por ejemplo, un vector es una matriz con una dimensión de 1), sin embargo, parece implicar que n es el ancho y la altura, y por lo tanto tiene elementos n² . Tendría más sentido que n sea el número de elementos, con n = w × h.
niXar
1
Aquí hay una forma rápida de hacerlo: almacene los índices de fila y columna (digamos i y j). La transposición lleva tiempo constante (solo intercambie los índices :). Puedes hacer lo mismo con las rotaciones (jugar con índices).
saadtaame
44
En caso de que n ^ 2 no sea factible. Puede crear una interfaz que acceda a cada elemento. Luego, dado (i, j), aplique la rotación para (i, j) acceder al elemento girado y regresar. Puede que no sea la mejor solución, pero funciona.
Confundir

Respuestas:

140

Aquí está en 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;
}
Nick Berardi
fuente
66
Claro, pero ¿qué pasa con una solución que usa memoria O (1)?
AlexeyMK
20
Su solución tiene O (n ^ 2) complejidad espacial. Necesito hacerlo mejor
Kshitij Jain
66
¿Qué tal para la matriz NXM?
Rohit
18
La complejidad es lineal en el número de elementos en la matriz. Si N es el número de elementos, la complejidad es O (N). Si N es la longitud del lado, entonces sí, la complejidad es O (N ^ 2), pero eso sigue siendo óptimo. Tienes que leer cada elemento al menos una vez. Imprimir la matriz es la misma complejidad
Alejandro
66
Para una rotación de -90 grados:ret[i][j] = matrix[j][n - i - 1]
Duncan Luk
387

O (n ^ 2) tiempo y O (1) algoritmo de espacio (¡sin ninguna solución y cosas de hanky-panky!)

Rotar por +90:

  1. Transponer
  2. Invierta cada fila

Rotar por -90:

Método 1 :

  1. Transponer
  2. Invierta cada columna

Método 2:

  1. Invierta cada fila
  2. Transponer

Rotar por +180:

Método 1 : gire +90 dos veces

Método 2 : Invierta cada fila y luego invierta cada columna (Transposición)

Rotar por -180:

Método 1 : rotar -90 dos veces

Método 2 : invierta cada columna y luego invierta cada fila

Método 3 : Rotar +180 ya que son lo mismo

hoyuelo
fuente
44
Esto fue muy útil para mí; Pude escribir un algoritmo una vez que supe la "versión del [pseudo-] código" de esta operación. ¡Gracias!
duma
13
Una de mis respuestas SO favoritas de todos los tiempos. Muy instructivo!
g33kz0r
2
Aquí hay una implementación de JavaScript JSFiddle si alguien está interesado.
Sr. Polywhirl
66
Rotar por -90: (1) Invierta cada fila; (2) Transponer. Haskell: rotateCW = map reverse . transposeyrotateCCW = transpose . map reverse
Thomas Eding
55
¿Cuál es la diferencia entre girar 180 y -180?
Qian Chen
178

Me gustaría agregar un poco más de detalle. En esta respuesta, los conceptos clave se repiten, el ritmo es lento e intencionalmente repetitivo. La solución proporcionada aquí no es la más sintácticamente compacta, sin embargo, está destinada a aquellos que desean aprender qué es la rotación de matrices y la implementación resultante.

En primer lugar, ¿qué es una matriz? Para los fines de esta respuesta, una matriz es solo una cuadrícula donde el ancho y la altura son iguales. Tenga en cuenta que el ancho y el alto de una matriz pueden ser diferentes, pero para simplificar, este tutorial considera solo matrices con igual ancho y alto ( matrices cuadradas ). Y sí, matrices es el plural de matrix.

Los ejemplos de matrices son: 2 × 2, 3 × 3 o 5 × 5. O, más generalmente, N × N. Una matriz 2 × 2 tendrá 4 cuadrados porque 2 × 2 = 4. Una matriz de 5 × 5 tendrá 25 cuadrados porque 5 × 5 = 25. Cada cuadrado se llama elemento o entrada. Representaremos cada elemento con un punto ( .) en los siguientes diagramas:

Matriz 2 × 2

. .
. .

Matriz 3 × 3

. . .
. . .
. . .

Matriz 4 × 4

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

Entonces, ¿qué significa rotar una matriz? Tomemos una matriz de 2 × 2 y pongamos algunos números en cada elemento para que se pueda observar la rotación:

0 1
2 3

Girar esto 90 grados nos da:

2 0
3 1

Literalmente giramos toda la matriz una vez hacia la derecha, como si girara el volante de un automóvil. Puede ser útil pensar en "inclinar" la matriz sobre su lado derecho. Queremos escribir una función, en Python, que tome una matriz y gire una vez hacia la derecha. La firma de la función será:

def rotate(matrix):
    # Algorithm goes here.

La matriz se definirá utilizando una matriz bidimensional:

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

Por lo tanto, la primera posición de índice accede a la fila. La segunda posición del índice accede a la columna:

matrix[row][column]

Definiremos una función de utilidad para imprimir una matriz.

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

Un método para rotar una matriz es hacerlo capa por capa. ¿Pero qué es una capa? Piensa en una cebolla. Al igual que las capas de una cebolla, a medida que se elimina cada capa, nos movemos hacia el centro. Otras analogías es una muñeca Matryoshka o un juego de pasar el paquete.

El ancho y la altura de una matriz dictan el número de capas en esa matriz. Usemos diferentes símbolos para cada capa:

Una matriz 2 × 2 tiene 1 capa

. .
. .

Una matriz 3 × 3 tiene 2 capas.

. . .
. x .
. . .

Una matriz 4 × 4 tiene 2 capas.

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

Una matriz de 5 × 5 tiene 3 capas.

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

Una matriz de 6 × 6 tiene 3 capas.

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

Una matriz de 7 × 7 tiene 4 capas.

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

Puede notar que al incrementar el ancho y la altura de una matriz en uno, no siempre aumenta el número de capas. Tomando las matrices anteriores y tabulando las capas y dimensiones, vemos que el número de capas aumenta una vez por cada dos incrementos de ancho y alto:

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

Sin embargo, no todas las capas necesitan rotación. Una matriz 1 × 1 es la misma antes y después de la rotación. La capa central 1 × 1 siempre es la misma antes y después de la rotación, sin importar qué tan grande sea la matriz general:

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

Dada la matriz N × N, ¿cómo podemos determinar mediante programación el número de capas que necesitamos rotar? Si dividimos el ancho o la altura entre dos e ignoramos el resto, obtenemos los siguientes resultados.

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

¿Observa cómo N/2coincide el número de capas que deben rotarse? A veces, el número de capas giratorias es uno menos el número total de capas en la matriz. Esto ocurre cuando la capa más interna está formada por un solo elemento (es decir, una matriz 1 × 1) y, por lo tanto, no necesita ser rotada. Simplemente se ignora.

Sin duda, necesitaremos esta información en nuestra función para rotar una matriz, así que añádala ahora:

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

Ahora que sabemos qué capas son y cómo determinar el número de capas que realmente necesitan rotación, ¿cómo aislamos una sola capa para poder rotarla? En primer lugar, inspeccionamos una matriz desde la capa más externa, hacia adentro, hasta la capa más interna. Una matriz de 5 × 5 tiene tres capas en total y dos capas que necesitan rotación:

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

Veamos primero las columnas. La posición de las columnas que definen la capa más externa, suponiendo que contamos desde 0, son 0 y 4:

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

0 y 4 también son las posiciones de las filas para la capa más externa.

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

Este siempre será el caso, ya que el ancho y la altura son los mismos. Por lo tanto, podemos definir las posiciones de columna y fila de una capa con solo dos valores (en lugar de cuatro).

Moviéndose hacia adentro a la segunda capa, la posición de las columnas es 1 y 3. Y, sí, lo adivinó, es lo mismo para las filas. Es importante comprender que tuvimos que aumentar y disminuir las posiciones de las filas y columnas al movernos hacia la siguiente capa.

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

Entonces, para inspeccionar cada capa, queremos un bucle con contadores crecientes y decrecientes que representen moverse hacia adentro, comenzando desde la capa más externa. Llamaremos a esto nuestro 'bucle de capa'.

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)

El código anterior recorre las posiciones (fila y columna) de cualquier capa que necesite rotación.

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

Ahora tenemos un bucle que proporciona las posiciones de las filas y columnas de cada capa. Las variables firste lastidentifican la posición del índice de las primeras y últimas filas y columnas. Volviendo a nuestras tablas de filas y columnas:

+--------+-----------+
| 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 | . . . . . |
+-----+-----------+

Entonces podemos navegar a través de las capas de una matriz. Ahora necesitamos una forma de navegar dentro de una capa para poder mover elementos alrededor de esa capa. Tenga en cuenta que los elementos nunca 'saltan' de una capa a otra, pero se mueven dentro de sus respectivas capas.

Al girar cada elemento en una capa, se rota toda la capa. Al girar todas las capas en una matriz, se rota toda la matriz. Esta oración es muy importante, así que por favor, intente comprenderla antes de continuar.

Ahora, necesitamos una forma de mover elementos, es decir, rotar cada elemento y, posteriormente, la capa y, finalmente, la matriz. Para simplificar, volveremos a una matriz de 3x3, que tiene una capa giratoria.

0 1 2
3 4 5
6 7 8

Nuestro bucle de capa proporciona los índices de la primera y la última columna, así como la primera y la última fila:

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

Debido a que nuestras matrices son siempre cuadradas, solo necesitamos dos variables firsty last, dado que las posiciones de índice son las mismas para filas y columnas.

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.

Las variables primero y último se pueden usar fácilmente para hacer referencia a las cuatro esquinas de una matriz. Esto se debe a que las esquinas en sí pueden definirse usando varias permutaciones de firsty last(sin sustracción, suma ni compensación de esas variables):

+---------------+-------------------+-------------+
| 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)       |
+---------------+-------------------+-------------+

Por esta razón, comenzamos nuestra rotación en las cuatro esquinas exteriores; las rotaremos primero. Destaquémoslos con *.

* 1 *
3 4 5
* 7 *

Queremos intercambiar cada una *con el *de la derecha de la misma. Así que sigamos imprimiendo nuestras esquinas definidas usando solo varias permutaciones de firsty 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)

La salida debe ser:

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

Ahora podríamos intercambiar fácilmente cada una de las esquinas desde nuestro bucle de capa:

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)

Matriz antes de girar esquinas:

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

Matriz después de rotar esquinas:

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

¡Excelente! Hemos rotado con éxito cada esquina de la matriz. Pero no hemos rotado los elementos en el medio de cada capa. Claramente, necesitamos una forma de iterar dentro de una capa.

El problema es que el único bucle en nuestra función hasta ahora (nuestro bucle de capa) se mueve a la siguiente capa en cada iteración. Como nuestra matriz tiene solo una capa giratoria, el bucle de capa sale después de rotar solo las esquinas. Veamos qué sucede con una matriz más grande de 5 × 5 (donde dos capas necesitan rotación). El código de función se ha omitido, pero sigue siendo el mismo que el anterior:

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)

El resultado es:

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

No debería sorprender que las esquinas de la capa más externa se hayan girado, pero también puede notar que las esquinas de la siguiente capa (hacia adentro) también se han girado. Esto tiene sentido. Hemos escrito código para navegar por las capas y también para rotar las esquinas de cada capa. Esto se siente como un progreso, pero desafortunadamente debemos dar un paso atrás. Simplemente no es bueno pasar a la siguiente capa hasta que la capa anterior (exterior) se haya rotado por completo. Es decir, hasta que cada elemento en la capa haya sido girado. ¡Girar solo las esquinas no funcionará!

Tomar una respiración profunda. Necesitamos otro bucle. Un bucle anidado no menos. El nuevo bucle anidado utilizará las variables firsty last, más un desplazamiento para navegar dentro de una capa. Llamaremos a este nuevo bucle nuestro 'bucle de elementos'. El bucle de elementos visitará cada elemento a lo largo de la fila superior, cada elemento en el lado derecho, cada elemento en la fila inferior y cada elemento en el lado izquierdo.

  • Avanzar a lo largo de la fila superior requiere que se incremente el índice de la columna.
  • Moverse hacia el lado derecho requiere que se incremente el índice de fila.
  • Moverse hacia atrás a lo largo de la parte inferior requiere que se disminuya el índice de la columna.
  • Mover hacia arriba por el lado izquierdo requiere que se disminuya el índice de la fila.

Esto suena complejo, pero se hace fácil porque el número de veces que incrementamos y disminuimos para lograr lo anterior sigue siendo el mismo en los cuatro lados de la matriz. Por ejemplo:

  • Mueve 1 elemento a través de la fila superior.
  • Mueve 1 elemento hacia abajo por el lado derecho.
  • Mueva 1 elemento hacia atrás a lo largo de la fila inferior.
  • Mueve 1 elemento hacia arriba del lado izquierdo.

Esto significa que podemos usar una sola variable en combinación con las variables firsty lastpara movernos dentro de una capa. Puede ser útil notar que moverse por la fila superior y hacia abajo por el lado derecho requiere un incremento. Mientras se mueve hacia atrás a lo largo de la parte inferior y hacia arriba del lado izquierdo, ambos requieren disminución.

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)

Ahora simplemente tenemos que asignar la parte superior al lado derecho, el lado derecho al fondo, el fondo al lado izquierdo y el lado izquierdo a la parte superior. Al poner todo esto juntos obtenemos:

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

Dada la matriz:

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

Nuestra rotatefunción da como resultado:

6,  3,  0  
7,  4,  1  
8,  5,  2  
Jack
fuente
Inicialmente me sentí como "wow, la mejor explicación", pero después de leerlo un par de veces (para asegurarme de que no me perdiera nada importante en el mar de palabras), mi opinión cambió a "hombre, lo entiendo, puedo lo mantenemos en movimiento por favor? " Todavía votó por tomar lo que debieron haber sido horas para componer una respuesta tan elaborada.
Abhijit Sarkar
1
@AbhijitSarkar - Gracias por votar y espero que al menos haya ayudado de alguna manera. Por supuesto, tienes razón, mi respuesta es prolija. Sin embargo, esto fue intencionalmente en contraste con la gran mayoría de las respuestas. Como dije al comienzo de mi respuesta: "En esta respuesta, los conceptos clave se repiten, el ritmo es lento e intencionalmente repetitivo". Si tiene ediciones que mantienen la claridad y la repetición necesaria pero reducen el número de palabras, estoy muy abierto a sugerencias. O simplemente editar :)
Jack
@jack Muy buena explicación. Sin embargo, no pude entender, ¿cómo se te ocurrió offset = element - first y last = size - first - 1? ¿Te cuesta entender esto? Además, ¿el último desplazamiento es igual al desplazamiento?
ashishjmeshram
1
TL; DR:list(zip(*reversed(your_list_of_lists)))
Boris
127

Pitón:

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

y en sentido antihorario:

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

Cómo funciona esto:

zip(*original)intercambiará ejes de matrices 2d al apilar los elementos correspondientes de las listas en nuevas listas. (El *operador le dice a la función que distribuya las listas contenidas en argumentos)

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

La [::-1]declaración invierte los elementos de la matriz (consulte Slices extendidos o esta pregunta ):

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

Finalmente, combinar los dos dará como resultado la transformación de rotación.

El cambio en la ubicación de [::-1]invertirá listas en diferentes niveles de la matriz.

bcdan
fuente
3
Creo que este código se origina en Peter Norvig: norvig.com/python-iaq.html
Josip
Puede usar en zip(*reversed(original))lugar de zip(*original[::-1])evitar crear una copia adicional de la lista original.
Boris
70

Aquí hay uno que hace la rotación en su lugar en lugar de usar una matriz completamente nueva para mantener el resultado. Dejé la inicialización de la matriz y la imprimí. Esto solo funciona para matrices cuadradas, pero pueden ser de cualquier tamaño. La sobrecarga de memoria es igual al tamaño de un elemento de la matriz para que pueda hacer la rotación de una matriz tan grande como desee.

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;
    }
}
dagorym
fuente
Puedo ver al menos un error. Si va a publicar código, pruébelo o al menos diga que no lo ha hecho.
Hugh Allen
1
¿Dónde? Señalarlo y lo arreglaré. Lo probé y funcionó bien en matrices pares e impares.
dagorym
2
Es una hermosa solución. La mente puede realizar tales hazañas si se establece a propósito. de O (n2) a O (1)
MoveFast
2
No es O (1); sigue siendo O (n ^ 2)
duma
11
Es O (n ^ 2) con memoria O (1).
Neel
38

Aquí hay toneladas de código bueno, pero solo quiero mostrar lo que está sucediendo geométricamente para que pueda comprender un poco mejor la lógica del código. Así es como abordaría esto.

En primer lugar, no confunda esto con la transposición, que es muy fácil.

La idea básica es tratarlo como capas y rotar una capa a la vez.

decimos que tenemos un 4x4

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

después de rotarlo en el sentido de las agujas del reloj 90, obtenemos

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

así que descompongamos esto, primero rotamos las 4 esquinas esencialmente

1           4


13          16

luego rotamos el siguiente diamante que está torcido

    2
            8
9       
        15

y luego el segundo diamante sesgado

        3
5           
            12
    14

de modo que se cuide el borde exterior, así que esencialmente hacemos ese caparazón a la vez hasta

finalmente el cuadrado del medio (o si es extraño, solo el elemento final que no se mueve)

6   7
10  11

así que ahora descubramos los índices de cada capa, supongamos que siempre trabajamos con la capa más externa, estamos haciendo

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

y así sucesivamente hasta que estemos a la mitad del borde

así que en general el patrón es

[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]
ajustes
fuente
¿Qué significa "a mitad del borde"? Veo muchos algoritmos en bucle hasta N / 2 y otros en bucle hasta N, pero no puedo ver de dónde viene el N / 2.
PDN
Creo que es la misma solución que se da al descifrar la entrevista de codificación. Pero me gusta la explicación paso a paso. Muy agradable y minucioso.
Naphstor
@PDN Esta respuesta lo explica en detalle.
Mathias Bynens
35

Como dije en mi publicación anterior, aquí hay un código en C # que implementa una rotación de matriz O (1) para matrices de cualquier tamaño. Para mayor brevedad y legibilidad, no hay verificación de errores o verificación de rango. El código:

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

Bien, levantaré la mano, en realidad no hace ninguna modificación en la matriz original al rotar. Pero, en un sistema OO que no importa siempre y cuando el objeto parezca que se ha girado a los clientes de la clase. Por el momento, la clase Matrix utiliza referencias a los datos de la matriz original, por lo que cambiar cualquier valor de m1 también cambiará m2 y m3. Un pequeño cambio en el constructor para crear una nueva matriz y copiar los valores en él lo resolverá.

Skizz
fuente
44
¡Bravo! Esta es una solución muy buena y no sé por qué no es la respuesta aceptada.
Martinatime
@martinatime: tal vez porque es 5 veces más grande
Toad
@Toad: Bueno, escribir código siempre es una compensación entre los requisitos de la competencia: velocidad, tamaño, costo, etc.
Skizz
15
cierto ... otro problema es el hecho de que la matriz de hecho no está rotada, sino que gira 'justo a tiempo'. Lo cual es excelente para acceder a algunos elementos, pero sería horrible si esta matriz se usara en cálculos o manipulaciones de imágenes. Entonces decir O (1) no es realmente justo.
Sapo
23

Si bien puede ser necesario rotar los datos en su lugar (quizás para actualizar la representación almacenada físicamente), se vuelve más simple y posiblemente más eficaz agregar una capa de indirección en el acceso a la matriz, tal vez una interfaz:

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

Si Matrixya implementa esta interfaz, puede rotarla a través de una clase de decorador como esta:

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

Rotar + 90 / -90 / 180 grados, voltear horizontalmente / verticalmente y escalar también se puede lograr de esta manera.

El rendimiento debería medirse en su escenario específico. Sin embargo, la operación O (n ^ 2) ahora ha sido reemplazada por una llamada O (1). Es una llamada de método virtual que es más lenta que el acceso directo a la matriz, por lo que depende de la frecuencia con la que se usa la matriz rotada después de la rotación. Si se usa una vez, entonces este enfoque definitivamente ganaría. Si se gira y luego se usa en un sistema de larga duración durante días, la rotación en el lugar podría funcionar mejor. También depende de si puede aceptar el costo inicial.

Como con todos los problemas de rendimiento, ¡mida, mida, mida!

Drew Noakes
fuente
1
+1 ... Y si la matriz es realmente grande y solo accedes a un par de elementos (uso escaso), es aún más eficaz
lothar
16
Parece un poco injusto llamar a esto una solución de tiempo O (1). Para resolver el problema planteado por el OP, esto todavía llevará tiempo O (n ^ 2). No solo eso, no resolvería el problema porque devuelve la transposición . El ejemplo dado no tiene la transposición como solución.
Vlad el Impala
55
Ahora, si todo lo que quería eran los primeros 3 elementos de la matriz, esta es una buena solución, pero el problema es recuperar una matriz completamente transformada (es decir, suponiendo que necesita todos los elementos de la matriz). Llamar a esto O (1) es el método de Credit Default Swap de Algorithm Analysis: no ha resuelto el problema, simplemente se lo ha
Ana Betts
44
@Paul Betts: entiendo su punto, pero como escribí anteriormente en los comentarios, incluso si realmente tiene la matriz transpuesta, aún tiene que escribir el bucle si desea leer los valores. Por lo tanto, leer todos los valores de una matriz siempre es O (N ^ 2) independientemente. La diferencia aquí es que si transpone, gira, escala, escala nuevamente, etc., entonces solo tomará el golpe O (N ^ 2) una vez. Como dije, esta no siempre es la mejor solución, pero en muchos casos es apropiada y vale la pena. El OP parecía estar buscando una solución mágica, y esto es lo más cerca que podrá llegar.
Drew Noakes
99
Me gusta esta respuesta, pero quiero señalar algo. Imprimir la matriz decorada (y hacer otras lecturas secuenciales en general) puede ser mucho más lento que hacer lo mismo con una matriz que se ha girado en la memoria, y no es solo por las llamadas a métodos virtuales. Para una matriz grande, va a aumentar enormemente la cantidad de errores de caché que obtiene al leer "abajo" en lugar de "a través".
Mike Daniels
18

Esta es una mejor versión en Java: lo he hecho para una matriz con un ancho y una altura diferentes

  • h es aquí la altura de la matriz después de girar
  • w es aquí el ancho de la matriz después de girar

 

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

Este código se basa en la publicación de Nick Berardi.

Martijn Courteaux
fuente
Gracias. Este fue el código Java más claro aquí. Pregunta: ¿Cómo se le ocurrió a Nick / usted la parte [w - j - 1]? Mirando la respuesta de @tweaking, puedo ver cómo puede derivar eso a través de ejemplos de inducción / resolución. Solo me pregunto si así es como se obtuvo o si se basa en algún principio matemático relacionado con Matrices.
Quest Monger
17

Ruby-way: .transpose.map &:reverse

Nakilon
fuente
1
Es incluso más simple que eso: array.reverse.transposegira una matriz en sentido horario, mientras que la array.transpose.reversegira en sentido antihorario. No hay necesidad de eso map.
Giorgi Gzirishvili
13

Ya hay muchas respuestas, y encontré dos que reclaman la complejidad del tiempo O (1). El algoritmo real O (1) es dejar el almacenamiento de matriz intacto y cambiar la forma en que indexa sus elementos. El objetivo aquí es que no consume memoria adicional, ni requiere tiempo adicional para iterar los datos.

Las rotaciones de 90, -90 y 180 grados son transformaciones simples que se pueden realizar siempre que sepa cuántas filas y columnas hay en su matriz 2D; Para rotar cualquier vector 90 grados, intercambie los ejes y niegue el eje Y. Para -90 grados, intercambie los ejes y niegue el eje X. Para 180 grados, niegue ambos ejes sin intercambiar.

Son posibles otras transformaciones, como la duplicación horizontal y / o vertical al negar los ejes de forma independiente.

Esto se puede hacer, por ejemplo, mediante un método de acceso. Los ejemplos a continuación son funciones de JavaScript, pero los conceptos se aplican por igual a todos los idiomas.

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

Este código supone una matriz de matrices anidadas, donde cada matriz interna es una fila.

El método le permite leer (o escribir) elementos (incluso en orden aleatorio) como si la matriz se hubiera girado o transformado. Ahora simplemente elija la función correcta para llamar, probablemente por referencia, ¡y listo!

El concepto se puede ampliar para aplicar transformaciones de forma aditiva (y no destructiva) a través de los métodos de acceso. Incluyendo rotaciones de ángulo arbitrarias y escalado.

Jason Oster
fuente
Sin embargo, ninguno de estos realmente rotó de la matriz original. El primero, el resultado final simplemente se transpone. El segundo, parece que acaba de barajar las filas o reflejar en el centro horizontal. El tercero, solo invirtió las filas y el cuarto también se transpuso. Ninguno de los cuales en realidad fue "rotado".
SM177Y
Hay algunos errores en los últimos dos ejemplos. Trivial para arreglar. Señalé explícitamente que esta solución no es una rotación in situ. Es una función de transformación, que la hace adecuada para la iteración perezosa.
Jason Oster
Excepto que no hay rotación, por lo que en realidad no respondió lo que le preguntó el OP.
SM177Y
@ SM177Y Otro editor agregó un código de ejemplo que no funciona a mi respuesta. Puedo ver cómo te confundiste. He corregido los errores en los bucles de iteración. De hecho, las funciones proporcionadas "rotan" los datos en las matrices.
Jason Oster
También un detalle importante es que el código de ejemplo realmente borra la respuesta original que proporcioné, que estaba tratando de ilustrar el poder de las transformaciones funcionales sobre las soluciones de complejidad lineal espacio-tiempo. Con una transformación funcional eres ya iterando o accediendo a los elementos de la matriz , por lo que la transformación se considera "libre" en el sentido de la complejidad constante del espacio y el tiempo.
Jason Oster
10

Un par de personas ya han presentado ejemplos que implican hacer una nueva matriz.

Algunas otras cosas a considerar:

(a) En lugar de mover los datos, simplemente atraviese la matriz "rotada" de manera diferente.

(b) Hacer la rotación en el lugar puede ser un poco más complicado. Necesitarás un poco de lugar para rascar (probablemente aproximadamente igual a una fila o columna de tamaño). Hay un antiguo documento de ACM sobre hacer transposiciones en el lugar ( http://doi.acm.org/10.1145/355719.355729 ), pero su código de ejemplo es FORTRAN desagradable cargado de goto.

Apéndice:

http://doi.acm.org/10.1145/355611.355612 es otro algoritmo de transposición in situ supuestamente superior.

nsanders
fuente
Estoy de acuerdo con ésto. Tenga un método que determine la traducción entre los datos de origen y los datos "rotados".
Martinatime
8

De Nick respuesta también funcionaría para una matriz NxM con solo una pequeña modificación (a diferencia de una 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];

Una forma de pensar en esto es que ha movido el centro del eje (0,0) desde la esquina superior izquierda a la esquina superior derecha. Simplemente estás transponiendo de uno a otro.

Kevin Berridge
fuente
6

Tiempo - O (N), Espacio - 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;
        }
    }
}
usuario2793692
fuente
Esto no es O (1). Esto es O (n).
Jason Oster
@JasonOster Creo que este es el espacio O (1), ya que no consume espacio adicional.
incipiente
@ffledgling Mi error. O (1) complejidad espacial, sí. O (n) complejidad de tiempo.
Jason Oster
La complejidad espacial es O (n) también. La complejidad del espacio debe incluir el espacio del tamaño variable de entrada. careercup.com/question?id=14952322
Jason Heo
¿Cómo podría modificar esto para que funcione para una rotación en sentido antihorario?
MD XF
5

Aquí está mi versión de Ruby (tenga en cuenta que los valores no se muestran de la misma manera, pero todavía gira como se describe).

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

La salida:

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
Mike Stone
fuente
4

Aquí hay un método de rotación en el espacio, por java, solo para el cuadrado. para una matriz 2D no cuadrada, deberá crear una nueva matriz de todos modos.

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

código para rotar cualquier tamaño de matriz 2D creando una nueva matriz:

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;
}
James Yu
fuente
3

Implementación del pseudocódigo +90 de dimple (por ejemplo, transponer y luego invertir cada fila) en 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;
}
mhawksey
fuente
3

Puedes hacer esto en 3 sencillos pasos :

1 ) Supongamos que tenemos una matriz

   1 2 3
   4 5 6
   7 8 9

2 ) Tomar la transposición de la matriz

   1 4 7
   2 5 8
   3 6 9

3 ) Intercambiar filas para obtener la matriz girada

   3 6 9
   2 5 8
   1 4 7

Código fuente de Java para esto:

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

Salida:

1 2 3 
4 5 6 
7 8 9 

3 6 9 
2 5 8 
1 4 7 
Prateek Joshi
fuente
2

Esta es mi implementación, en complejidad de memoria C, O (1), rotación en el lugar, 90 grados en sentido horario:

#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;
        }
    }
}
Araña
fuente
2

Aquí está la versión de 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;
        }
    }
}

El método primero gira la capa más externa, luego se mueve secuencialmente a la capa interna.

radio
fuente
2

Desde un punto de vista lineal, considere las matrices:

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

Ahora toma una transposición

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

Y considere la acción de A 'sobre B, o B sobre A'.
Respectivamente:

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

Esto es expandible para cualquier matriz nxn. Y aplicando este concepto rápidamente en código:

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);
        }
    }
}
Señor nex
fuente
2

Código C # para rotar [n, m] matrices 2D 90 grados a la derecha

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

Resultado:

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

Desde PHP5.6, la transposición de matriz se puede realizar con una array_map()llamada lenta . En otras palabras, las columnas se convierten en filas.

Código: ( Demo )

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

$ transpuesto:

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

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

X es el tamaño de la matriz en la que se encuentra el gráfico.

turco
fuente
1

#transpose es un método estándar de la clase Ruby's Array, por lo tanto:

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

La implementación es una función de transposición n ^ 2 escrita en C. Puede verla aquí: http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-transpose "clic para alternar la fuente "al lado de" transponer ".

Recuerdo mejores soluciones que O (n ^ 2), pero solo para matrices especialmente construidas (como matrices dispersas)

k00ka
fuente
1

Código C para la rotación de la matriz 90 grados en sentido horario EN EL LUGAR para cualquier matriz 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;
        }
    }
}
rohitmb
fuente
1

Aquí está mi implementación en el lugar en 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;
        }
    }
}
thiagoh
fuente
1

Aquí está mi intento de rotación de la matriz de 90 grados, que es una solución de 2 pasos en C. Primero transponga la matriz en su lugar y luego intercambie los cols.

#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;
}
usuario1596193
fuente
1

@dagorym: Aw, hombre. Había estado colgándome de esto como un buen acertijo "Estoy aburrido, qué puedo reflexionar". Se me ocurrió mi código de transposición in situ, pero llegué aquí para encontrar el suyo casi idéntico al mío ... ah, bueno. Aquí está en 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
Nathan Fritz
fuente
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];
  }
}

Método simple de C ++, aunque habría una gran sobrecarga de memoria en una gran matriz.

Guillermo
fuente
Entre todas estas respuestas he encontrado y probado esta que es compacta y suficiente para rotar
dlewin