İki boyutlu bir diziyi nasıl döndürürsünüz?


302

Raymond Chen'in gönderisinden esinlenerek , 4x4 iki boyutlu bir diziniz olduğunu söyleyin, 90 derece döndüren bir işlev yazın. Raymond sözde kodda bir çözüme bağlanır, ancak bazı gerçek dünya şeyleri görmek istiyorum.

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

Oluyor:

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

Güncelleme : Nick'in cevabı en basit olanıdır, ancak bunu n ^ 2'den daha iyi yapmanın bir yolu var mı? Matris 10000x10000 olsaydı ne olurdu?


99
N ^ 2'den daha azıyla nasıl kurtulabilirsiniz? Tüm öğeler okunmalı ve ayarlanmalıdır ve n ^ 2 öğe vardır
erikkallen


9
N nedir? 2B dizinin kare olup olmadığını söylemezsiniz (genel durumda değildir! Örneğin, bir vektör 1 boyutuna sahip bir matristir), ancak n'nin genişlik ve yükseklik olduğunu ve bu nedenle n² öğelerine sahip olduğunu ima ediyorsunuz. . N'nin eleman sayısı olması daha anlamlı olacaktır, n = w × h.
niXar

1
İşte bunu yapmanın hızlı bir yolu: satır ve sütun indekslerini saklayın (örneğin i ve j). Transpoz sabit zaman alır (sadece endeksleri değiştirin :). Aynısını rotasyonlarla da yapabilirsiniz (endekslerle oyna).
saadtaame

4
N ^ 2 mümkün değilse. Her bir öğeye erişen bir arayüz oluşturabilirsiniz. Daha sonra (i, j) verildiğinde, (i, j) döndürülmüş öğeye erişmek ve geri dönmek için döndürme uygulayın. En iyi çözüm olmayabilir ama işe yarar.
04

Yanıtlar:


140

İşte 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
Tabii, ama O (1) bellek kullanan bir çözüme ne dersiniz?
AlexeyMK

20
Çözümünüzün O (n ^ 2) alan karmaşıklığı vardır. Daha iyi yapmalıyız
Kshitij Jain

6
NXM matrisi için ne dersiniz?
Rohit

17
Karmaşıklık, dizideki öğelerin sayısında doğrusaldır. N eleman sayısı ise karmaşıklık O (N) olur. N kenar uzunluğuysa, evet, karmaşıklık O (N ^ 2) 'dir, ancak bu yine de en uygunudur. Her öğeyi en az bir kez okumalısınız. Matrisi yazdırmak aynı karmaşıklıktır
Alejandro

6
-90 derece dönüş için:ret[i][j] = matrix[j][n - i - 1]
Duncan Luk

387

O (n ^ 2) zaman ve O (1) uzay algoritması (herhangi bir geçici çözüm ve hanky-panky şeyler olmadan!)

+90 döndürme:

  1. Transpoze
  2. Her satırı ters çevir

-90 ile döndür:

Yöntem 1 :

  1. Transpoze
  2. Her sütunu ters çevir

Yöntem 2:

  1. Her satırı ters çevir
  2. Transpoze

+180 döndür:

Yöntem 1 : +90 tarafından iki kez döndürme

Yöntem 2 : Her satırı ters çevirin ve sonra her sütunu ters çevirin (Devrik)

-180 döndürün:

Yöntem 1 : -90 ile iki kez döndürme

Yöntem 2 : Her sütunu ters çevirin ve ardından her satırı ters çevirin

Yöntem 3 : Aynı oldukları gibi +180 döndürün


4
Bu benim için çok yardımcı oldu; Bu işlemin "[sözde kod sürümü” nü bildiğimde algoritma yazmayı başardım. Teşekkürler!
duma

13
Tüm zamanların en sevdiğim SO cevaplarından biri. Çok öğretici!
g33kz0r

2
İşte ilgilenen varsa bir JavaScript uygulaması JSFiddle .
Bay Polywhirl

6
-90 ile döndürün: (1) Her satırı ters çevirin; (2) Geçiş. Haskell: rotateCW = map reverse . transposeverotateCCW = transpose . map reverse
Thomas Eding

5
180 ve -180 döndürme arasındaki fark nedir?
Qian Chen

177

Biraz daha ayrıntı eklemek istiyorum. Bu cevapta, anahtar kavramlar tekrarlanır, hız yavaş ve kasıtlı olarak tekrarlanır. Burada sağlanan çözüm en sözdizimsel olarak kompakt değildir, ancak matris dönüşünün ne olduğunu ve sonuçta ortaya çıkan uygulamayı öğrenmek isteyenler için tasarlanmıştır.

İlk olarak, matris nedir? Bu cevabın amaçları doğrultusunda, bir matris sadece genişlik ve yüksekliğin aynı olduğu bir ızgaradır. Bir matrisin genişliği ve yüksekliği farklı olabilir, ancak basitlik açısından bu öğretici yalnızca eşit genişlik ve yüksekliğe sahip matrisleri ( kare matrisler ) dikkate alır. Ve evet, matrisler matrisin çoğuludur.

Örnek matrisler şunlardır: 2 × 2, 3 × 3 veya 5 × 5. Veya daha genel olarak N × N'dir. 2 × 2 matris 4 kareye sahip olacaktır, çünkü 2 × 2 = 4. 5 × 5 matris 25 kareye sahip olacaktır, çünkü 5 × 5 = 25. Her kareye bir eleman veya giriş denir. Her öğeyi bir nokta ile temsil edeceğiz (. aşağıdaki diyagramlarda ) :

2 × 2 matris

. .
. .

3 × 3 matris

. . .
. . .
. . .

4 × 4 matris

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

Peki, bir matrisi döndürmek ne anlama geliyor? 2 × 2 matrisini alalım ve her öğeye bazı sayılar koyalım, böylece dönme gözlemlenebilir:

0 1
2 3

Bunu 90 derece döndürmek bize şunu verir:

2 0
3 1

Kelimenin tam anlamıyla bir arabanın direksiyonunu çevirmek gibi bir kez sağa çevirdik. Matrisi sağ tarafına "yatırmayı" düşünmek yardımcı olabilir. Python'da, bir matris alan ve bir kez sağa dönen bir fonksiyon yazmak istiyoruz. İşlev imzası:

def rotate(matrix):
    # Algorithm goes here.

Matris iki boyutlu bir dizi kullanılarak tanımlanacaktır:

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

Bu nedenle, ilk dizin konumu satıra erişir. İkinci dizin konumu sütuna erişir:

matrix[row][column]

Bir matris yazdırmak için bir yardımcı program işlevi tanımlayacağız.

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

Bir matrisi döndürmenin bir yöntemi, her seferinde bir katman yapmaktır. Peki katman nedir? Bir soğan düşünün. Tıpkı bir soğan tabakası gibi, her tabaka çıkarıldıkça merkeze doğru hareket ediyoruz. Diğer analojiler bir Matryoshka bebeği veya parselin bir geçiş oyunudur.

Bir matrisin genişliği ve yüksekliği, o matristeki katman sayısını belirler. Her katman için farklı semboller kullanalım:

2 × 2 matrisin 1 katmanı vardır

. .
. .

3 × 3 matrisin 2 katmanı vardır

. . .
. x .
. . .

4 × 4 matrisin 2 katmanı vardır

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

5 × 5 matrisin 3 katmanı vardır

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

6 × 6 matrisin 3 katmanı vardır

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

7 × 7 matrisin 4 katmanı vardır

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

Bir matrisin genişliğini ve yüksekliğini birer birer artırmanın, katman sayısını her zaman artırmadığını fark edebilirsiniz. Yukarıdaki matrisleri alarak katmanları ve boyutları tablo haline getirerek, genişlik ve yüksekliğin her iki artışı için katman sayısının bir kez arttığını görüyoruz:

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

Ancak, tüm katmanların dönmesine gerek yoktur. 1 × 1 matris, dönmeden önce ve sonra aynıdır. Merkezi 1 × 1 katman, genel matris ne kadar büyük olursa olsun, döndürmeden önce ve sonra her zaman aynıdır:

+-----+--------+------------------+
| 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 matrisi verildiğinde, döndürmemiz gereken katman sayısını programsal olarak nasıl belirleyebiliriz? Genişliği veya yüksekliği ikiye bölerek kalanını görmezden gelirsek aşağıdaki sonuçları elde ederiz.

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

Nasıl olduğunu fark et N/2Döndürülmesi gereken katman sayısıyla eşleştiğine ? Bazen dönebilen katman sayısı, matristeki toplam katman sayısının bir katıdır. Bu, en içteki katman sadece bir elemandan (yani 1 × 1 matris) oluştuğunda ve dolayısıyla döndürülmeleri gerekmediğinde meydana gelir. Sadece görmezden gelinir.

Şüphesiz bir matrisi döndürmek için işlevimizde bu bilgilere ihtiyacımız olacak, bu yüzden şimdi ekleyelim:

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

Şimdi hangi katmanların ne olduğunu ve gerçekten dönmesi gereken katman sayısını nasıl belirleyeceğimizi biliyoruz, tek bir katmanı nasıl döndürebiliriz, böylece onu döndürebiliriz? İlk olarak, en dıştaki katmandan içe, en içteki katmana kadar bir matrisi inceliyoruz. 5 × 5 matrisin toplam üç katmanı ve dönmesi gereken iki katmanı vardır:

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

Önce sütunlara bakalım. 0'dan saydığımızı varsayarak, en dış katmanı tanımlayan sütunların konumu 0 ve 4'tür:

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

O ve 4, en dıştaki kat için sıraların konumlandırmasıdır.

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

Genişlik ve yükseklik aynı olduğu için bu her zaman geçerli olacaktır. Bu nedenle, bir katmanın sütun ve satır konumlarını yalnızca dört değerle (dört yerine) tanımlayabiliriz.

İkinci katmana doğru ilerlerken, sütunların konumu 1 ve 3'tür. Ve evet, tahmin ettiniz, satırlar için aynı. Bir sonraki katmana içe doğru hareket ederken satır ve sütun konumlarını hem artırmamız hem de azaltmamız gerektiğini anlamak önemlidir.

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

Bu nedenle, her bir katmanı incelemek için, en dış katmandan başlayarak içeri doğru hareket etmeyi temsil eden hem artan hem de azalan sayaçlara sahip bir döngü istiyoruz. Buna 'katman döngüsü' diyeceğiz.

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)

Yukarıdaki kod, döndürülmesi gereken katmanların (satır ve sütun) konumlarında dolaşır.

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

Artık her katmanın satır ve sütunlarının konumlarını sağlayan bir döngümüz var. Değişkenler firstve lastilk ve son satır ve sütunların dizin konumunu tanımlar. Satır ve sütun tablolarımıza bakın:

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

Böylece bir matrisin katmanları arasında gezinebiliriz. Şimdi, bir katman içinde gezinme yoluna ihtiyacımız var, böylece elemanları o katmanın etrafında hareket ettirebiliriz. Not: Öğeler hiçbir zaman bir katmandan diğerine 'zıplamaz', ancak ilgili katmanları içinde hareket ederler.

Katmandaki her öğeyi döndürmek tüm katmanı döndürür. Bir matristeki tüm katmanları döndürmek tüm matrisi döndürür. Bu cümle çok önemlidir, bu yüzden lütfen devam etmeden önce anlamak için elinizden gelenin en iyisini yapın.

Şimdi, elemanları hareket ettirmenin bir yoluna ihtiyacımız var, yani her bir elemanı ve daha sonra katmanı ve sonuçta matrisi döndürmek. Basitlik için, döndürülebilir bir katmanı olan 3x3'lük bir matrise geri döneceğiz.

0 1 2
3 4 5
6 7 8

Katman döngümüz, ilk ve son sütunların yanı sıra ilk ve son satırların dizinlerini sağlar:

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

Matrislerimiz her zaman kare olduğu için sadece iki değişkene ihtiyacımız var firstve lastdizin konumları satırlar ve sütunlar için aynı olduğundan.

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.

İlk ve son değişkenler bir matrisin dört köşesine başvuruda bulunmak için kolayca kullanılabilir. Bu, muhtelif permütasyon kullanılarak tanımlanabilir köşeleri için firstve last(bir çıkarma, ekleme ya da bu değişkenlerin ofset):

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

Bu nedenle rotasyonumuza dış dört köşeden başlıyoruz - önce bunları döndüreceğiz. Onları ile vurgulayalım *.

* 1 *
3 4 5
* 7 *

Her birini sağındaki *ile değiştirmek istiyoruz *. Yani sadece çeşitli permütasyon kullanılarak tanımlanır Şimdi devam eden köşeleri dışarı baskı izin firstve 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)

Çıktı şöyle olmalıdır:

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

Artık her bir köşeyi katman döngümüzden kolayca değiştirebiliriz:

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)

Köşeleri döndürmeden önce matris:

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

Köşeleri döndürdükten sonra matris:

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

Harika! Matrisin her köşesini başarıyla döndürdük. Ancak, her katmanın ortasındaki öğeleri döndürmedik. Açıkça bir katman içinde yineleme yöntemine ihtiyacımız var.

Sorun şu ki, fonksiyonumuzdaki tek döngü (katman döngümüz), her yinelemede bir sonraki katmana geçiyor. Matrisimiz yalnızca bir dönebilir katmana sahip olduğundan, katman döngüsü sadece köşeleri döndürdükten sonra çıkar. Daha büyük, 5 × 5 bir matrisle (iki katın dönmesi gereken yerlerde) neler olduğuna bakalım. İşlev kodu atlandı, ancak yukarıdakiyle aynı kaldı:

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)

Çıktı:

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

En dış katmanın köşelerinin döndürülmüş olması şaşırtıcı olmamalı, ancak bir sonraki katmanın (içe doğru) köşelerinin de döndürüldüğünü fark edebilirsiniz. Bu mantıklı. Katmanlar arasında gezinmek ve her katmanın köşelerini döndürmek için kod yazdık. Bu ilerleme gibi hissettiriyor, ancak maalesef geri adım atmalıyız. Önceki (dış) katman tamamen döndürülünceye kadar bir sonraki katmana geçmek iyi değildir. Yani, katmandaki her eleman döndürülünceye kadar. Sadece köşeleri döndürmek işe yaramaz!

Derin bir nefes al. Başka bir döngüye ihtiyacımız var. Yuvalanmış bir döngü daha az değil. Yeni, iç içe döngü, firstve lastdeğişkenlerini artı bir katman içinde gezinmek için bir uzaklık kullanır. Bu yeni döngüye 'element loop' diyoruz. Eleman döngüsü, her bir elemanı üst sıra boyunca, her bir eleman sağ tarafa, her bir eleman alt sıra boyunca ve her bir eleman sol tarafa doğru ziyaret edecektir.

  • Üst sıra boyunca ileriye doğru hareket etmek için sütun dizininin artırılması gerekir.
  • Sağ tarafa gitmek için satır dizininin artırılması gerekir.
  • Altta geriye doğru hareket etmek için sütun dizininin azaltılması gerekir.
  • Sol tarafı yukarı taşımak satır sırasının azaltılmasını gerektirir.

Bu karmaşık görünüyor, ancak bu kolaylaştı çünkü yukarıdakilere ulaşmak için arttırma ve azaltma sayımız matrisin dört tarafında da aynı kalıyor. Örneğin:

  • 1 elemanı üst sıra boyunca hareket ettirin.
  • 1 elemanı sağ tarafa doğru hareket ettirin.
  • 1 elemanı alt satır boyunca geriye doğru hareket ettirin.
  • 1 elemanı sol tarafa doğru hareket ettirin.

Bu, bir katman içinde hareket etmek için firstve lastdeğişkenleriyle birlikte tek bir değişken kullanabileceğimiz anlamına gelir . Üst sıra boyunca ve sağ tarafta aşağı doğru hareket etmenin her ikisinin de artırılması gerektiğini belirtmek yardımcı olabilir. Altta ve sol tarafta yukarı doğru hareket ederken her ikisi de azaltılmasını gerektirir.

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)

Şimdi sadece üst tarafı sağ tarafa, sağ tarafı alt tarafa, alt taraf sol tarafa ve sol tarafı üst tarafa atamamız gerekiyor. Tüm bunları bir araya getirdiğimizde:

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

Matris verildiğinde:

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

Bizim rotatefonksiyonumuz:

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

Başlangıçta "vay, şimdiye kadarki en iyi açıklama" gibi hissettim, ama birkaç kez okuduktan sonra (kelimelerin denizinde önemli bir şey kaçırmadım emin olmak için), fikrim "adam, anladım, olabilir devam edelim lütfen? " Böyle ayrıntılı bir cevap oluşturmak için saat olması gereken şeyleri hala kabul ettik.
Abhijit Sarkar

1
@AbhijitSarkar - Oy verdiğiniz için teşekkürler ve umarım en azından küçük bir şekilde yardımcı olmuştur. Tabii ki haklısın, cevabım garip. Ancak bu, kasten cevapların büyük çoğunluğunun aksine idi. Cevabımın en başında söylediğim gibi: "Bu cevapta, anahtar kavramlar tekrarlanır, hız yavaş ve kasıtlı olarak tekrarlanır." Açıklığı ve gerekli tekrarları koruyan ancak kelime sayısını azaltan düzenlemeleriniz varsa, önerilere çok açığım. Veya sadece düzenle :)
Jack

@jack Gerçekten iyi bir açıklama. Ancak, anlayamadım, offset = element - first and last = size - first - 1'i nasıl buldunuz? Bunu anlamakta zorlanıyor musunuz? Ayrıca, son ofset, ofset ile aynı mıdır?
ashishjmeshram

1
TL; DR:list(zip(*reversed(your_list_of_lists)))
Boris

127

Python:

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

ve saat yönünün tersine:

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

Bu nasıl çalışır:

zip(*original)ilgili öğeleri listelerden yeni listelere istifleyerek 2d dizilerinin eksenlerini değiştirir. ( *İşleç , işleve içerilen listeleri bağımsız değişkenlere dağıtmasını söyler)

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

[::-1]Deyimi tersine çevirir dizi elemanları (lütfen Dilimler Extended veya bu soru ):

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

Son olarak, ikisini birleştirmek dönüş dönüşümü ile sonuçlanacaktır.

Yerleşimindeki değişiklik [::-1], matrisin farklı düzeylerindeki listeleri tersine çevirir.


3
Bu kodun Peter Norvig'den
Josip

Sen kullanabilirsiniz zip(*reversed(original))yerine zip(*original[::-1])orijinal listenin fazladan bir kopyasını oluşturarak önlemek için.
Boris

70

İşte sonucu tutmak için tamamen yeni bir dizi kullanmak yerine rotasyonu yerinde yapan bir. Dizinin başlatılmasını ve yazdırılmasını bıraktım. Bu yalnızca kare diziler için geçerlidir, ancak herhangi bir boyutta olabilirler. Bellek ek yükü, dizinin bir öğesinin boyutuna eşittir, böylece istediğiniz kadar büyük bir dizinin dönüşünü yapabilirsiniz.

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

En az bir hata görebiliyorum. Kod gönderecekseniz, test edin veya en azından yapmadığınızı söyleyin.
Hugh Allen

1
Nerede? Göster ve ben hallederim. Test ettim ve hem tek hem de boyutlu dizilerde iyi çalıştı.
dagorym

2
güzel bir çözüm. Zihin, amaca yönelik olarak ayarlandığında bu tür başarıları gerçekleştirebilir. (n2) - O (1) arası
MoveFast

2
O (1) değil; hala O (n ^ 2)
duma

11
O (n ^ 2) hafızalı O (1).
Neel

38

Burada iyi kod ton var ama sadece geometrik olarak neler olduğunu göstermek istiyorum, böylece kod mantığını biraz daha iyi anlayabilirsiniz. İşte buna nasıl yaklaşacağım.

her şeyden önce, bunu çok kolay olan aktarma ile karıştırmayın.

temel fikir katman olarak ele almak ve her seferinde bir katman döndürmektir.

4x4'ümüz var diyelim

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

saat yönünde 90 derece döndürdükten sonra

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

hadi bunu parçalayalım, önce 4 köşeyi esasen döndürelim

1           4


13          16

sonra bir çeşit çarpık olan aşağıdaki elması döndürüyoruz

    2
            8
9       
        15

ve sonra 2. çarpık elmas

        3
5           
            12
    14

böylece dış kenara dikkat eder, yani o kabuğu bir kerede

nihayet orta kare (veya sadece hareket etmeyen son eleman garip ise)

6   7
10  11

Şimdi her katmanın indekslerini bulalım, her zaman en dış katmanla çalıştığımızı varsayalım,

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

kenarın yarısına kadar

genel olarak desen

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

"kenarın yarısında" ne anlama geliyor? N / 2 ve diğerleri N kadar döngü kadar algoritmalar bir sürü görüyorum, ama N / 2 nereden geliyor göremiyorum.
PDN

Kodlama röportajını kırmada verilen çözümün aynı olduğuna inanıyorum. Ama adım adım açıklamayı seviyorum. Çok güzel ve titiz.
Naphstor

@PDN Bu cevap ayrıntılı olarak açıklamaktadır.
Mathias Bynens

35

Önceki yazımda söylediğim gibi, C # 'da herhangi bir boyut matrisi için bir O (1) matris dönüşü uygulayan bazı kodlar. Kısa ve okunabilirlik için hata kontrolü veya aralık kontrolü yoktur. Kod:

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

Tamam, elimi kaldırırım, aslında dönerken orijinal dizide herhangi bir değişiklik yapmaz. Ancak, nesne sınıfın istemcilerine döndürülmüş gibi göründüğü sürece önemli olmayan bir OO sisteminde. Şu anda, Matrix sınıfı orijinal dizi verilerine referanslar kullanır, böylece m1'in herhangi bir değerini değiştirmek de m2 ve m3'ü değiştirir. Yeni bir dizi oluşturmak ve değerleri bu dizine kopyalamak için yapıcıda yapılan küçük bir değişiklik bunu sıralar.


4
Bravo! Bu çok güzel bir çözüm ve neden kabul edilen cevap olmadığını bilmiyorum.
martinatime

@martinatime: belki 5 kat büyük olduğu için
Toad

@Toad: Yazma kodu her zaman rakip gereksinimler arasında bir takastır: hız, boyut, maliyet, vb.
Skizz

15
true ... başka bir sorun, matrisin aslında döndürülmemiş olması değil, 'tam zamanında' döndürülmesidir. Bu, birkaç öğeye erişmek için harika, ancak bu matris hesaplamalarda veya görüntü manipülasyonlarında kullanıldığında korkunç olurdu. Yani O (1) demek gerçekten adil değil.
Kurbağa

23

Verileri yerinde döndürmek gerekli olsa da (belki de fiziksel olarak saklanan gösterimi güncellemek için), dizi erişimine, belki de bir arayüze bir dolaylı katman eklemek daha basit ve muhtemelen daha performanslıdır:

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

Eğer senin Matrixzaten bu arayüzü uygulayan, o zaman aracılığıyla döndürülmüş olabilir dekoratör böyle sınıfa:

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 derece döndürme, yatay / dikey çevirme ve ölçeklendirme de bu şekilde elde edilebilir.

Performansın özel senaryoda ölçülmesi gerekir. Ancak O (n ^ 2) işlemi artık bir O (1) çağrısı ile değiştirildi. Bu sanal bir yöntem çağrısı var olduğunu bu yüzden döndürülmüş dizi dönüşten sonra nasıl kullanıldığını sıklıkla bağlıdır, doğrudan dizi erişimi daha yavaş. Bir kez kullanılırsa, bu yaklaşım kesinlikle kazanacaktır. Döndürülürse, günlerce uzun süre çalışan bir sistemde kullanılırsa, yerinde dönüş daha iyi performans gösterebilir. Ayrıca, ön maliyeti kabul edip edemeyeceğinize de bağlıdır.

Tüm performans konularında olduğu gibi, ölçün, ölçün, ölçün!


1
+1 ... Ve eğer matris gerçekten büyükse ve sadece birkaç elemana (seyrek kullanım)
erişiyorsanız,

16
Buna O (1) zaman çözümü demek biraz haksızlık gibi görünüyor. OP'nin yarattığı sorunu çözmek için bu hala O (n ^ 2) zaman alacaktır. Sadece bu da değil, bu sorunu çözmez çünkü devrikliği döndürür . Verilen örnek, çözüm olarak aktarıma sahip değildir.
Vlad the Impala

5
Şimdi, istediğiniz tek şey matrisin ilk 3 elementiyse , bu iyi bir çözümdür, ancak sorun tamamen dönüştürülmüş bir matrisi almaktır (yani tüm matris elemanlarına ihtiyacınız olduğunu varsayarak ). Bunu O (1) olarak adlandırmak, Algoritma Analizinin Kredi Varsayılan Değiştirme yöntemidir - sorunu çözmediniz, sadece bir başkasına ittiniz :)
Ana Betts

4
@Paul Betts: Demek istediğim, ama yukarıda yorumlarda yazdığım gibi, aslında matris aktarılmış olsa bile, değerleri okumak istiyorsanız yine de döngüyü yazmanız gerekir. Yani bir matristen tüm değerleri okumak her zaman O (N ^ 2) 'dir. Buradaki fark, eğer transpoze ederseniz, döndürürseniz, ölçeklendirirseniz, tekrar ölçeklendirirseniz, vb. Dediğim gibi, bu her zaman en iyi çözüm değildir, ancak çoğu durumda uygun ve faydalıdır. OP sihirli bir çözüm arıyor gibi görünüyordu ve bu alacağınız kadar yakın.
Drew Noakes

9
Bu yanıtı seviyorum, ama bir şeyi belirtmek istiyorum. Süslü matrisi yazdırmak (ve genel olarak diğer sıralı okumaları yapmak), aynısını bellekte döndürülen bir matrise yapmaktan çok daha yavaş olabilir ve bu sadece sanal yöntem çağrıları nedeniyle değildir. Büyük bir matris için, "karşı" yerine "aşağı" okuyarak aldığınız önbellek kaçırma sayısını büyük ölçüde artıracaksınız.
Mike Daniels

18

Bu Java'da daha iyi bir versiyonu: Farklı bir genişlik ve yükseklikte bir matris için yaptım

  • h burada döndükten sonra matrisin yüksekliği
  • w burada döndükten sonra matrisin genişliği

 

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

Bu kod Nick Berardi'nin gönderisine dayanmaktadır.


Teşekkürler. Buradaki en açık Java koduydu. Soru - Siz / Nick [w - j - 1] bölümünü nasıl buldunuz? @Tweaking cevabına baktığımda bunu indüksiyon / çözme örnekleriyle nasıl türetebileceğinizi görebilirsiniz. Sadece nasıl elde edildiğini merak mı yoksa Matrisler ile ilgili bazı matematiksel prensibe dayalı mı?
Görev Monger

17

Yakut yönlü: .transpose.map &:reverse


1
Bundan daha da basit: array.reverse.transposebir diziyi saat yönünde, array.transpose.reversesaat yönünün tersine döndürür. Gerek yok map.
Giorgi Gzirishvili

13

Şimdiden birçok cevap var ve ben O (1) zaman karmaşıklığını iddia eden iki tane buldum. gerçek Ç (1) algoritması bakir dizi depolama bırakın ve nasıl endeksi unsurları değiştirmektir. Buradaki amaç, ek bellek tüketmemesi veya verileri yinelemek için ek zaman gerektirmemesidir.

90, -90 ve 180 derecelik dönüşler, 2D dizinizde kaç satır ve sütun olduğunu bildiğiniz sürece gerçekleştirilebilen basit dönüşümlerdir; Herhangi bir vektörü 90 derece döndürmek için eksenleri değiştirin ve Y eksenini kaldırın. -90 derece için eksenleri değiştirin ve X eksenini kaldırın. 180 derece için, değiştirmeden her iki ekseni de ortadan kaldırın.

Eksenleri bağımsız olarak yok ederek yatay ve / veya dikey aynalama gibi başka dönüşümler de mümkündür.

Bu, örneğin bir erişimci yöntemi ile yapılabilir. Aşağıdaki örnekler JavaScript işlevleridir, ancak kavramlar tüm diller için aynı şekilde geçerlidir.

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

Bu kod, her iç dizinin bir satır olduğu bir dizi iç içe diziyi varsayar.

Yöntem, dizi döndürülmüş veya dönüştürülmüş gibi öğeleri (rastgele sırada bile) okumanıza (veya yazmanıza) olanak tanır. Şimdi, sadece referans olarak aramak için doğru işlevi seçin ve gidip gidin!

Bu kavram, dönüşümleri erişimci yöntemlerle ek olarak (ve tahribatsız olarak) uygulamak için genişletilebilir. Keyfi açı dönüşleri ve ölçeklendirme dahil.


Bunların hiçbiri aslında orijinal diziden dönmemiştir. İlki, sonuç basitçe iç hukuka aktarılır. İkincisi, sadece sıraları karıştırmış veya yatay merkez boyunca yansıtılmış gibi görünüyorsunuz. Üçüncüsü, yalnızca satırları tersine çevirdiniz ve dördüncüsü de aktarıldı. Bunların hiçbiri aslında "döndürülmedi".
SM177Y

Son iki örnekte bazı hatalar var. Düzeltmek önemsiz. Bu çözümün yerinde bir rotasyon olmadığını açıkça belirtmiştim . Tembel iterasyon için uygun hale getiren bir dönüşüm fonksiyonudur.
Jason Oster

Dönme dışında OP'nin sorduğu soruya cevap vermediniz.
SM177Y

@ SM177Y Başka bir editör cevabıma çalışmayan örnek kod ekledi. Onunla nasıl karıştırıldığını görebiliyorum. Yineleme döngülerindeki hataları düzelttim. Sağlanan işlevler aslında dizilerdeki verileri "döndürür".
Jason Oster

Ayrıca önemli ayrıntı, örnek kodun, sağladığım orijinal cevabı gerçekten yıkamasıdır ki bu, fonksiyonel dönüşümlerin doğrusal uzay-zaman karmaşıklığı çözümleri üzerindeki gücünü göstermeye çalışıyordu. İşlevsel bir dönüşümle , dizi öğelerini zaten yineliyor veya başka bir şekilde erişiyorsunuz , bu nedenle dönüşüm, sabit alan ve zaman karmaşıklığı anlamında "serbest" olarak kabul ediliyor.
Jason Oster

10

Birkaç kişi yeni bir dizi oluşturmayı içeren örnekler ortaya koydu.

Dikkate alınması gereken birkaç şey daha:

(a) Verileri gerçekte taşımak yerine, "döndürülmüş" diziyi farklı şekilde hareket ettirin.

(b) Rotasyonun yerinde yapılması biraz daha zor olabilir. Biraz çizik yere ihtiyacınız olacak (muhtemelen kabaca bir satır veya sütuna eşit). Yerinde aktarma yapmakla ilgili eski bir ACM makalesi var ( http://doi.acm.org/10.1145/355719.355729 ), ancak örnek kodları kötü goto yüklü FORTRAN.

Zeyilname:

http://doi.acm.org/10.1145/355611.355612 başka bir, sözde üstün, yerinde aktarma algoritmasıdır.


Buna katılıyorum. Kaynak veriler ile "döndürülmüş" veriler arasındaki çeviriyi belirleyen bir yöntem kullanın.
martinatime

8

Nick'in yanıtı, bir NxM dizisi için de sadece küçük bir değişiklikle (NxN yerine) işe yarayacaktır.

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

Bunu düşünmenin bir yolu, eksenin merkezini (0,0) sol üst köşeden sağ üst köşeye taşımış olmanızdır. Sadece birinden diğerine aktarıyorsunuz.


6

Zaman - O (N), Boşluk - 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;
        }
    }
}

Bu O (1) değil. Bu O (n).
Jason Oster

@JasonOster Bu alanın O (1) alan olduğuna inanıyorum, çünkü fazladan yer kaplamaz.
14'te

@ffledgling Benim hatam. O (1) uzay karmaşıklığı, evet. O (n) zaman karmaşıklığı.
Jason Oster

Uzay Karmaşıklığı da O (n) 'dir. Boşluk Karmaşıklığı girdi değişken büyüklüğünün boşluğunu içermelidir. kariyercup.com/question?id=14952322
Jason Heo

Bunu saat yönünün tersine döndürmek için nasıl değiştirebilirim?
MD XF

5

İşte benim Ruby sürümüm (değerlerin aynı görünmediğini, ancak yine de açıklandığı gibi döndüğünü unutmayın).

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

Çıktı:

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

burada, java tarafından sadece kare için bir uzayda döndürme yöntemi. kare olmayan 2d dizisi için yine de yeni dizi oluşturmanız gerekir.

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

yeni dizi oluşturarak herhangi bir boyut 2d dizisini döndürmek için kod:

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'ın +90 sahte kodunun (ör. Her satırı transpoze edin, sonra ters çevirin) JavaScript'te uygulanması:

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

Bunu 3 kolay adımda yapabilirsiniz :

1 ) Bir matrisimiz olduğunu varsayalım

   1 2 3
   4 5 6
   7 8 9

2 ) Matrisin devri

   1 4 7
   2 5 8
   3 6 9

3 ) Döndürülmüş matris elde etmek için satırları değiştirin

   3 6 9
   2 5 8
   1 4 7

Bunun için Java kaynak kodu :

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

Çıktı:

1 2 3 
4 5 6 
7 8 9 

3 6 9 
2 5 8 
1 4 7 

2

Bu benim uygulama, C, O (1) bellek karmaşıklığı, yerinde döndürme, saat yönünde 90 derece:

#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

İşte Java sürümü:

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

yöntem önce mostouter katmanını döndürür, sonra iç katmana doğru bir şekilde hareket eder.


2

Doğrusal bir bakış açısından, matrisleri düşünün:

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

Şimdi A devrik al

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

Ve A'nın B üzerindeki veya B'nin A üzerindeki etkisini düşünün.
Sırasıyla:

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

Bu herhangi bir nxn matrisi için genişletilebilir. Ve bu kavramı hızlı bir şekilde kodda uygulamak:

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 # kodu döndürmek için [n, m] 2D diziler 90 derece sağa

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

Sonuç:

    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'dan, dizi aktarımı ince bir array_map()çağrı ile gerçekleştirilebilir . Başka bir deyişle, sütunlar satırlara dönüştürülür.

Kod: ( Demo )

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

aktarılmamıştır, $:

[
    [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, grafiğin bulunduğu dizinin boyutudur.


1

#transpose, Ruby'nin Array sınıfının standart bir yöntemidir, bu nedenle:

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

Uygulama C ile yazılmış bir n ^ 2 transpozisyon fonksiyonudur. Burada görebilirsiniz: http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-transpose "tıklayın "devrik" yanındaki kaynağı "değiştirmek".

O (n ^ 2) çözeltilerinden daha iyi hatırlıyorum, ancak sadece özel olarak inşa edilmiş matrisler için (seyrek matrisler gibi)


1

Herhangi bir M * N matrisi için saat yönünde 90 derece saat yönünde matris dönüşü için C kodu

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

İşte benim C yerinde uygulama

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

Burada, C'de 2 aşamalı bir çözüm olan 90 derece döndürme matrisi için girişimim. Önce matrisi yerine aktarın ve daha sonra kovanları değiştirin.

#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: Ah adamım. Bunu iyi bir "sıkıldım, ne düşünebilirim" bulmacası olarak takmıştım. Yerinde aktarma kodumu buldum, ama seninkini benimkiyle hemen hemen aynı bulmak için geldim ... ah, iyi. İşte Ruby'de.

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

Basit C ++ yöntemi, tho büyük bir dizide büyük bir bellek yükü olurdu.


Tüm bu cevaplar arasında, kompakt ve döndürmek için yeterli olanı buldum ve test ettim
dlewin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.