Inspirado no post de Raymond Chen , diga que você tem uma matriz bidimensional 4x4, escreva uma função que a gire 90 graus. Raymond vincula-se a uma solução em pseudo-código, mas eu gostaria de ver algumas coisas do mundo real.
[1][2][3][4]
[5][6][7][8]
[9][0][1][2]
[3][4][5][6]
Torna-se:
[3][9][5][1]
[4][0][6][2]
[5][1][7][3]
[6][2][8][4]
Atualização : a resposta de Nick é a mais direta, mas existe uma maneira de fazer isso melhor que n ^ 2? E se a matriz fosse 10000x10000?
algorithm
matrix
multidimensional-array
swilliams
fonte
fonte
Respostas:
Aqui está em c #
fonte
ret[i][j] = matrix[j][n - i - 1]
Algoritmo O (n ^ 2) e O (1) espaço (sem nenhuma solução alternativa e coisas enlouquecedoras!)
Gire em +90:
Gire em -90:
Método 1 :
Método 2:
Gire em +180:
Método 1 : girar +90 duas vezes
Método 2 : reverter cada linha e, em seguida, reverter cada coluna (transpor)
Gire -180:
Método 1 : Gire -90 duas vezes
Método 2 : reverter cada coluna e, em seguida, reverter cada linha
Método 3 : Gire em +180, pois são iguais
fonte
rotateCW = map reverse . transpose
erotateCCW = transpose . map reverse
Eu gostaria de adicionar um pouco mais de detalhes. Nesta resposta, conceitos-chave são repetidos, o ritmo é lento e intencionalmente repetitivo. A solução fornecida aqui não é a mais sintaticamente compacta; é, no entanto, destinada a quem deseja aprender o que é a rotação da matriz e a implementação resultante.
Em primeiro lugar, o que é uma matriz? Para os fins desta resposta, uma matriz é apenas uma grade em que a largura e a altura são iguais. Observe que a largura e a altura de uma matriz podem ser diferentes, mas, para simplificar, este tutorial considera apenas matrizes com largura e altura iguais ( matrizes quadradas ). E sim, matrizes é o plural de matriz.
As matrizes de exemplo são: 2 × 2, 3 × 3 ou 5 × 5. Ou, mais geralmente, N × N. Uma matriz 2 × 2 terá 4 quadrados porque 2 × 2 = 4. Uma matriz 5 × 5 terá 25 quadrados porque 5 × 5 = 25. Cada quadrado é chamado de elemento ou entrada. Representaremos cada elemento com um ponto (
.
) nos diagramas abaixo:Matriz 2 × 2
Matriz 3 × 3
Matriz 4 × 4
Então, o que significa girar uma matriz? Vamos pegar uma matriz 2 × 2 e colocar alguns números em cada elemento para que a rotação possa ser observada:
Girar isso em 90 graus nos dá:
Nós literalmente giramos a matriz toda uma vez para a direita, assim como girar o volante de um carro. Pode ajudar a "inclinar" a matriz para o lado direito. Queremos escrever uma função, em Python, que pega uma matriz e gira uma vez para a direita. A assinatura da função será:
A matriz será definida usando uma matriz bidimensional:
Portanto, a primeira posição do índice acessa a linha. A segunda posição do índice acessa a coluna:
Definiremos uma função utilitária para imprimir uma matriz.
Um método de girar uma matriz é fazer uma camada de cada vez. Mas o que é uma camada? Pense em uma cebola. Assim como as camadas de uma cebola, à medida que cada camada é removida, avançamos em direção ao centro. Outras analogias são uma boneca matryoshka ou um jogo de empacotar.
A largura e a altura de uma matriz determinam o número de camadas nessa matriz. Vamos usar símbolos diferentes para cada camada:
Uma matriz 2 × 2 possui 1 camada
Uma matriz 3 × 3 possui 2 camadas
Uma matriz 4 × 4 tem 2 camadas
Uma matriz 5 × 5 possui 3 camadas
Uma matriz 6 × 6 tem 3 camadas
Uma matriz 7 × 7 tem 4 camadas
Você pode perceber que incrementar a largura e a altura de uma matriz em uma unidade nem sempre aumenta o número de camadas. Tomando as matrizes acima e tabulando as camadas e dimensões, vemos o número de camadas aumentar uma vez a cada dois incrementos de largura e altura:
No entanto, nem todas as camadas precisam ser rotacionadas. Uma matriz 1 × 1 é a mesma antes e depois da rotação. A camada 1 × 1 central é sempre a mesma antes e depois da rotação, independentemente do tamanho da matriz geral:
Dada a matriz N × N, como podemos determinar programaticamente o número de camadas que precisamos rotacionar? Se dividirmos a largura ou a altura por dois e ignorarmos o restante, obteremos os seguintes resultados.
Note como
N/2
corresponde ao número de camadas que precisam ser giradas? Às vezes, o número de camadas rotativas é um a menos o número total de camadas na matriz. Isso ocorre quando a camada mais interna é formada por apenas um elemento (ou seja, uma matriz 1 × 1) e, portanto, não precisa ser girada. Simplesmente é ignorado.Indubitavelmente, precisaremos dessas informações em nossa função para girar uma matriz, então vamos adicioná-las agora:
Agora sabemos o que são camadas e como determinar o número de camadas que realmente precisam ser rotacionadas. Como isolamos uma única camada para podermos rotacioná-la? Primeiro, inspecionamos uma matriz da camada mais externa, para dentro, até a camada mais interna. Uma matriz 5 × 5 possui três camadas no total e duas camadas que precisam ser rotacionadas:
Vejamos as colunas primeiro. A posição das colunas que definem a camada mais externa, assumindo que contamos de 0, são 0 e 4:
0 e 4 também são as posições das linhas da camada mais externa.
Esse sempre será o caso, pois a largura e a altura são as mesmas. Portanto, podemos definir as posições da coluna e da linha de uma camada com apenas dois valores (em vez de quatro).
Indo para a segunda camada, a posição das colunas é 1 e 3. E, sim, você adivinhou, é o mesmo para as linhas. É importante entender que tivemos que incrementar e diminuir as posições de linha e coluna ao mover para dentro para a próxima camada.
Portanto, para inspecionar cada camada, queremos um loop com contadores crescentes e decrescentes que representem o movimento para dentro, a partir da camada mais externa. Vamos chamar isso de nosso 'loop de camada'.
O código acima percorre as posições (linha e coluna) de todas as camadas que precisam ser rotacionadas.
Agora temos um loop fornecendo as posições das linhas e colunas de cada camada. As variáveis
first
elast
identificam a posição do índice da primeira e da última linhas e colunas. Voltando às nossas tabelas de linhas e colunas:Para que possamos navegar pelas camadas de uma matriz. Agora, precisamos de uma maneira de navegar dentro de uma camada para que possamos mover elementos ao redor dessa camada. Observe que os elementos nunca 'pulam' de uma camada para outra, mas se movem dentro de suas respectivas camadas.
Girar cada elemento em uma camada gira a camada inteira. A rotação de todas as camadas em uma matriz gira a matriz inteira. Esta frase é muito importante, então tente o seu melhor para entendê-la antes de prosseguir.
Agora, precisamos de uma maneira de realmente mover elementos, ou seja, girar cada elemento e, posteriormente, a camada e, finalmente, a matriz. Por simplicidade, reverteremos para uma matriz 3x3 - que possui uma camada rotativa.
Nosso loop de camada fornece os índices da primeira e da última coluna, bem como a primeira e a última linha:
Como nossas matrizes são sempre quadradas, precisamos de apenas duas variáveis
first
elast
, como as posições do índice são as mesmas para linhas e colunas.As variáveis first e last podem ser facilmente usadas para referenciar os quatro cantos de uma matriz. Isso ocorre porque os próprios cantos podem ser definidos usando várias permutações de
first
elast
(sem subtração, adição ou deslocamento dessas variáveis):Por esse motivo, iniciamos nossa rotação nos quatro cantos externos - nós os rodaremos primeiro. Vamos destacá-los com
*
.Queremos trocar cada
*
um*
pela direita. Então, vamos em frente e imprima nossos cantos definidos usando apenas várias permutações defirst
elast
:A saída deve ser:
Agora, podemos facilmente trocar cada um dos cantos de dentro do nosso loop de camada:
Matriz antes de girar cantos:
Matriz depois de girar os cantos:
Ótimo! Giramos com sucesso cada canto da matriz. Mas não rotacionamos os elementos no meio de cada camada. Claramente, precisamos de uma maneira de iterar dentro de uma camada.
O problema é que o único loop em nossa função até agora (nosso loop de camada) passa para a próxima camada em cada iteração. Como nossa matriz possui apenas uma camada rotativa, o loop da camada sai após a rotação apenas dos cantos. Vejamos o que acontece com uma matriz 5 × 5 maior (onde duas camadas precisam ser rotacionadas). O código da função foi omitido, mas permanece o mesmo que acima:
A saída é:
Não deve ser surpresa que os cantos da camada mais externa tenham sido girados, mas você também pode observar que os cantos da próxima camada (para dentro) também foram girados. Isso faz sentido. Escrevemos código para navegar pelas camadas e também para girar os cantos de cada camada. Parece um progresso, mas infelizmente devemos dar um passo atrás. Não adianta passar para a próxima camada até que a camada anterior (externa) tenha sido totalmente girada. Ou seja, até que cada elemento da camada seja girado. Girar apenas os cantos não serve!
Respire fundo. Precisamos de outro loop. Um loop aninhado não menos. O novo loop aninhado usará as variáveis
first
elast
, além de um deslocamento para navegar dentro de uma camada. Vamos chamar esse novo loop de 'elemento loop'. O loop do elemento visitará cada elemento ao longo da linha superior, cada elemento no lado direito, cada elemento ao longo da linha inferior e cada elemento no lado esquerdo.Isso parece complexo, mas é fácil porque o número de vezes que aumentamos e diminuímos para alcançar o acima mencionado permanece o mesmo nos quatro lados da matriz. Por exemplo:
Isso significa que podemos usar uma única variável em combinação com as variáveis
first
elast
para mover-se dentro de uma camada. Pode ser útil observar que a movimentação pela linha superior e pelo lado direito requer um incremento. Enquanto se move para trás ao longo da parte inferior e da esquerda, ambos exigem decréscimo.Agora, basta atribuir o topo ao lado direito, o lado direito ao fundo, o fundo ao lado esquerdo e o lado esquerdo ao topo. Juntando tudo isso, obtemos:
Dada a matriz:
Nossa
rotate
função resulta em:fonte
list(zip(*reversed(your_list_of_lists)))
Pitão:
e anti-horário:
Como isso funciona:
zip(*original)
trocará eixos de matrizes 2D, empilhando itens correspondentes das listas em novas listas. (O*
operador diz à função para distribuir as listas contidas em argumentos)A
[::-1]
instrução inverte os elementos da matriz (consulte Fatias estendidas ou esta pergunta ):Finalmente, a combinação dos dois resultará na transformação de rotação.
A mudança no posicionamento de
[::-1]
listas invertidas em diferentes níveis da matriz.fonte
zip(*reversed(original))
vez dezip(*original[::-1])
evitar criar uma cópia extra da lista original.Aqui está um que faz a rotação no lugar em vez de usar uma matriz completamente nova para manter o resultado. Eu deixei de inicializar a matriz e imprimi-la. Isso funciona apenas para matrizes quadradas, mas elas podem ser de qualquer tamanho. A sobrecarga de memória é igual ao tamanho de um elemento da matriz, para que você possa fazer a rotação da matriz maior que desejar.
fonte
Existem muitos códigos bons aqui, mas eu só quero mostrar o que está acontecendo geometricamente, para que você possa entender um pouco melhor a lógica do código. Aqui está como eu abordaria isso.
Antes de tudo, não confunda isso com a transposição, que é muito fácil.
a idéia básica é tratá-lo como camadas e giramos uma camada por vez.
dizem que temos um 4x4
depois de girá-lo no sentido horário por 90, obtemos
então vamos decompor isso, primeiro giramos os 4 cantos essencialmente
então giramos o seguinte diamante, que é meio torto
e então o segundo diamante inclinado
para que cuide da borda externa, então essencialmente fazemos uma concha de cada vez até
finalmente o quadrado do meio (ou, se for estranho, apenas o elemento final que não se move)
então agora vamos descobrir os índices de cada camada, suponha que sempre trabalhemos com a camada mais externa, estamos fazendo
assim por diante até que estamos no meio da borda
então, em geral, o padrão é
fonte
Como eu disse no meu post anterior, aqui está um código em C # que implementa uma rotação da matriz O (1) para qualquer matriz de tamanho. Para concisão e legibilidade, não há verificação de erros ou verificação de alcance. O código:
OK, eu vou colocar minha mão, na verdade não faz nenhuma modificação na matriz original ao girar. Mas, em um sistema OO que não importa, desde que o objeto pareça ter sido rotacionado para os clientes da classe. No momento, a classe Matrix usa referências aos dados originais da matriz, portanto, alterar qualquer valor de m1 também mudará m2 e m3. Uma pequena alteração no construtor para criar uma nova matriz e copiar os valores para ela resolverá isso.
fonte
Embora a rotação dos dados no local possa ser necessária (talvez para atualizar a representação fisicamente armazenada), torna-se mais simples e possivelmente mais eficiente adicionar uma camada de indireção ao acesso ao array, talvez uma interface:
Se você
Matrix
já implementa essa interface, ela pode ser rotacionada por meio de uma classe decoradora como esta:Girar + 90 / -90 / 180 graus, inverter horizontalmente / verticalmente e dimensionar também podem ser alcançados dessa maneira.
O desempenho precisaria ser medido em seu cenário específico. No entanto, a operação O (n ^ 2) foi substituída por uma chamada O (1). É uma chamada de método virtual que é mais lenta que o acesso direto à matriz, portanto depende da frequência com que a matriz girada é usada após a rotação. Se for usado uma vez, então essa abordagem definitivamente venceria. Se for girado e usado em um sistema de longa execução por dias, a rotação no local poderá ter um desempenho melhor. Também depende se você pode aceitar o custo inicial.
Como em todos os problemas de desempenho, meça, meça, meça!
fonte
Esta é uma versão melhor em Java: criei uma matriz com largura e altura diferentes
Este código é baseado na postagem de Nick Berardi.
fonte
Ruby-way:
.transpose.map &:reverse
fonte
array.reverse.transpose
gira uma matriz no sentido horário, enquantoarray.transpose.reverse
gira no sentido anti-horário. Não há necessidademap
.Já existem muitas respostas e encontrei duas alegando complexidade de tempo O (1). O real algoritmo O (1) é deixar o armazenamento da matriz intocado e alterar a maneira como você indexa seus elementos. O objetivo aqui é que ele não consuma memória adicional, nem requer tempo adicional para iterar os dados.
Rotações de 90, -90 e 180 graus são transformações simples que podem ser executadas desde que você saiba quantas linhas e colunas existem em sua matriz 2D; Para girar qualquer vetor 90 graus, troque os eixos e negue o eixo Y. Para -90 graus, troque os eixos e negue o eixo X. Para 180 graus, negue os dois eixos sem trocar.
Outras transformações são possíveis, como espelhamento horizontal e / ou vertical, negando os eixos de forma independente.
Isso pode ser feito através, por exemplo, de um método acessador. Os exemplos abaixo são funções JavaScript, mas os conceitos se aplicam igualmente a todos os idiomas.
Esse código assume uma matriz de matrizes aninhadas, onde cada matriz interna é uma linha.
O método permite que você leia (ou grave) elementos (mesmo em ordem aleatória) como se a matriz tivesse sido rotacionada ou transformada. Agora, basta escolher a função certa para ligar, provavelmente por referência, e pronto!
O conceito pode ser estendido para aplicar transformações de forma aditiva (e não destrutiva) através dos métodos de acessador. Incluindo rotações arbitrárias de ângulo e escala.
fonte
Algumas pessoas já colocaram exemplos que envolvem a criação de uma nova matriz.
Algumas outras coisas a considerar:
(a) Em vez de realmente mover os dados, basta percorrer a matriz "rotacionada" de maneira diferente.
(b) Fazer a rotação no local pode ser um pouco mais complicado. Você precisará de um pouco de espaço inicial (provavelmente igual a uma linha ou coluna). Há um artigo antigo da ACM sobre a realização de transposições no local ( http://doi.acm.org/10.1145/355719.355729 ), mas o código de exemplo deles é o FORTRAN desagradável e carregado de goto.
Termo aditivo:
http://doi.acm.org/10.1145/355611.355612 é outro algoritmo de transposição local supostamente superior.
fonte
A resposta de Nick também funcionaria para uma matriz NxM com apenas uma pequena modificação (em oposição a uma NxN).
Uma maneira de pensar sobre isso é que você moveu o centro do eixo (0,0) do canto superior esquerdo para o canto superior direito. Você está simplesmente transpondo de um para o outro.
fonte
Tempo - O (N), Espaço - O (1)
fonte
Aqui está minha versão do Ruby (observe que os valores não são exibidos da mesma forma, mas ainda assim gira como descrito).
A saída:
fonte
aqui está um método de rotação no espaço, por java, apenas para quadrado. para uma matriz 2D não quadrada, você precisará criar uma nova matriz de qualquer maneira.
código para girar qualquer matriz 2D de tamanho criando uma nova matriz:
fonte
Implementação do pseudocódigo +90 da covinha (por exemplo, transpor e reverter cada linha) em JavaScript:
fonte
Você pode fazer isso em 3 etapas fáceis :
1 ) Suponha que tenhamos uma matriz
2 ) Faça a transposição da matriz
3 ) Troque linhas para obter a matriz rotacionada
Código-fonte Java para isso:
Resultado:
fonte
Esta é minha implementação, na complexidade da memória C, O (1), rotação no local, 90 graus no sentido horário:
fonte
Aqui está a versão do Java:
o método primeiro rotaciona a camada maisouter e depois move para a camada interna de maneira esquemática.
fonte
De um ponto de vista linear, considere as matrizes:
Agora faça uma transposição
E considere a ação de A 'em B ou B em A'.
Respectivamente:
Isso é expansível para qualquer matriz nxn. E aplicando esse conceito rapidamente no código:
fonte
Código C # para girar [n, m] matrizes 2D 90 graus à direita
Resultado:
fonte
PHP:
No PHP5.6, a transposição da matriz pode ser realizada com uma
array_map()
chamada lenta . Em outras palavras, as colunas são convertidas em linhas.Código: ( Demo )
$ transposto:
fonte
For i:= 0 to X do For j := 0 to X do graphic[j][i] := graphic2[X-i][j]
X é o tamanho da matriz em que o gráfico se encontra.
fonte
#transpose é um método padrão da classe Ruby Array, portanto:
A implementação é uma função de transposição n ^ 2 escrita em C. Você pode vê-la aqui: http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-transpose escolhendo "clique em" para alternar a fonte "ao lado de" transpor ".
Lembro-me melhor que as soluções O (n ^ 2), mas apenas para matrizes especialmente construídas (como matrizes esparsas)
fonte
Código C para rotação da matriz 90 graus no sentido horário NO LOCAL para qualquer matriz M * N
fonte
aqui está minha implementação no local em C
fonte
Aqui está minha tentativa de rotação de 90 graus da matriz, que é uma solução em duas etapas em C. Primeiro transponha a matriz no lugar e depois troque as colunas.
fonte
@dagorym: Ah, cara. Eu tinha me apegado a isso como um bom quebra-cabeça "Estou entediado, o que posso ponderar". Eu vim com meu código de transposição no local, mas cheguei aqui para encontrar o seu praticamente idêntico ao meu ... ah, bem. Aqui está em Ruby.
fonte
Método C ++ simples, embora houvesse uma grande sobrecarga de memória em uma grande matriz.
fonte