คุณจะหมุนอาร์เรย์สองมิติได้อย่างไร


302

แรงบันดาลใจจากการโพสต์ของ Raymond Chenสมมติว่าคุณมีอาร์เรย์แบบสองมิติ 4x4 เขียนฟังก์ชั่นที่หมุนได้ 90 องศา เรย์มอนด์เชื่อมโยงไปยังโซลูชันในโค้ดหลอก แต่ฉันต้องการเห็นบางสิ่งในโลกแห่งความเป็นจริง

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

กลายเป็น:

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

ปรับปรุง : คำตอบของนิคตรงไปตรงมาที่สุด แต่มีวิธีการทำดีกว่า n ^ 2 หรือไม่? เกิดอะไรขึ้นถ้าเมทริกซ์มีขนาด 10,000x10000?


99
คุณจะหนีน้อยกว่า n ^ 2 ได้อย่างไร? องค์ประกอบทั้งหมดจะต้องอ่านและตั้งค่าและมีองค์ประกอบ n ^ 2
erikkallen


9
คุณคืออะไร คุณไม่ได้บอกว่าถ้าอาร์เรย์ 2 มิติเป็นสี่เหลี่ยมจัตุรัส (ไม่ใช่ในกรณีทั่วไป! เช่นเวกเตอร์เป็นเมทริกซ์ที่มีหนึ่งมิติของ 1) แต่คุณดูเหมือนจะบอกว่า n คือความกว้างและความสูงดังนั้นจึงมีองค์ประกอบ² . มันจะสมเหตุสมผลมากกว่าหากมี n เป็นจำนวนองค์ประกอบโดยมี n = w × h
niXar

1
นี่คือวิธีที่รวดเร็วในการทำ: เก็บดัชนีแถวและคอลัมน์ (พูดว่า i และ j) Transpose ใช้เวลาคงที่ (เพียงสลับดัชนี :) คุณสามารถทำเช่นเดียวกันกับการหมุน (เล่นกับดัชนี)
saadtaame

4
ในกรณีที่ n ^ 2 ไม่สามารถทำได้ คุณสามารถสร้างส่วนต่อประสานที่เข้าถึงแต่ละองค์ประกอบได้ จากนั้นให้ (i, j), ใช้การหมุนกับ (i, j) เข้าถึงองค์ประกอบที่หมุนแล้วกลับมา อาจไม่ได้เป็น soln ที่ดีที่สุด แต่ได้ผล
สับสน

คำตอบ:


140

นี่คือใน C #

int[,] array = new int[4,4] {
    { 1,2,3,4 },
    { 5,6,7,8 },
    { 9,0,1,2 },
    { 3,4,5,6 }
};

int[,] rotated = RotateMatrix(array, 4);

static int[,] RotateMatrix(int[,] matrix, int n) {
    int[,] ret = new int[n, n];

    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            ret[i, j] = matrix[n - j - 1, i];
        }
    }

    return ret;
}

6
แน่นอน แต่วิธีการแก้ปัญหาโดยใช้หน่วยความจำ O (1)?
AlexeyMK

20
โซลูชันของคุณมีความซับซ้อนของพื้นที่ O (n ^ 2) ต้องทำดีกว่า
Kshitij Jain

6
วิธีการเกี่ยวกับเมทริกซ์ NXM?
Rohit

17
ความซับซ้อนเป็นเส้นตรงในจำนวนองค์ประกอบในอาร์เรย์ ถ้า N คือจำนวนขององค์ประกอบความซับซ้อนคือ O (N) ถ้า N คือความยาวด้านข้างใช่แล้วความซับซ้อนคือ O (N ^ 2) แต่นั่นก็ยังดีที่สุด คุณต้องอ่านทุกองค์ประกอบอย่างน้อยหนึ่งครั้ง การพิมพ์เมทริกซ์เป็น complexitiy เดียวกัน
Alejandro

6
สำหรับการหมุนที่ -90 องศา:ret[i][j] = matrix[j][n - i - 1]
Duncan Luk

387

เวลา O (n ^ 2) และอัลกอริธึมพื้นที่ O (1) (ไม่มีการแก้ไขปัญหาใด ๆ และสิ่งที่ไม่ดี!)

หมุนด้วย +90:

  1. ไขว้
  2. ย้อนกลับแต่ละแถว

หมุนด้วย -90:

วิธีที่ 1:

  1. ไขว้
  2. ย้อนกลับแต่ละคอลัมน์

วิธีที่ 2:

  1. ย้อนกลับแต่ละแถว
  2. ไขว้

หมุนด้วย +180:

วิธีที่ 1 : หมุนด้วย +90 สองครั้ง

วิธีที่ 2 : ย้อนกลับแต่ละแถวจากนั้นย้อนกลับแต่ละคอลัมน์ (แปลงข้อมูล)

หมุนโดย -180:

วิธีที่ 1 : หมุนด้วย -90 สองครั้ง

วิธีที่ 2 : ย้อนกลับแต่ละคอลัมน์แล้วย้อนกลับแต่ละแถว

วิธีที่ 3 : หมุนด้วย +180 เหมือนกัน


4
สิ่งนี้มีประโยชน์มากสำหรับฉัน ฉันสามารถเขียนอัลกอริทึมได้เมื่อฉันรู้จัก "[หลอกหลอก] รหัสรุ่น" ของการดำเนินการนี้ ขอบคุณ!
duma

13
หนึ่งในคำตอบ SO ที่ฉันโปรดปรานตลอดกาล ให้คำแนะนำมาก!
g33kz0r

2
นี่คือการใช้งาน JavaScript JSFiddleถ้าใครสนใจ
นาย Polywhirl

6
หมุนตาม -90: (1) สลับแต่ละแถว; (2) ขนย้าย Haskell: rotateCW = map reverse . transposeและrotateCCW = transpose . map reverse
Thomas Eding

5
ความแตกต่างระหว่างการหมุน 180 และ -180 คืออะไร?
Qian Chen

177

ฉันต้องการเพิ่มรายละเอียดเพิ่มเติมอีกเล็กน้อย ในคำตอบนี้แนวคิดที่สำคัญซ้ำแล้วซ้ำอีกก้าวช้าและจงใจซ้ำ ๆ วิธีการแก้ปัญหาที่ให้ไว้ที่นี่ไม่ได้มีขนาดกะทัดรัดที่สุด syntactically แต่มีไว้สำหรับผู้ที่ต้องการเรียนรู้ว่าการหมุนเมทริกซ์คืออะไรและการใช้งานที่เกิดขึ้น

ประการแรกเมทริกซ์คืออะไร? สำหรับจุดประสงค์ของคำตอบเมทริกซ์เป็นเพียงกริดที่ความกว้างและความสูงเท่ากัน หมายเหตุความกว้างและความสูงของเมทริกซ์อาจแตกต่างกัน แต่สำหรับความเรียบง่ายแบบฝึกหัดนี้จะพิจารณาเฉพาะเมทริกซ์ที่มีความกว้างและความสูงเท่ากัน ( เมทริกซ์สี่เหลี่ยม ) ใช่แล้วเมทริกซ์คือพหูพจน์ของเมทริกซ์

ตัวอย่างการฝึกอบรมคือ: 2 × 2, 3 × 3 หรือ 5 × 5 หรือโดยทั่วไปแล้ว N × N เมทริกซ์ 2 × 2 จะมี 4 กำลังสองเนื่องจาก 2 × 2 = 4 เมทริกซ์ 5 × 5 จะมี 25 กำลังสองเพราะ 5 × 5 = 25 แต่ละตารางเรียกว่าองค์ประกอบหรือรายการ เราจะแสดงแต่ละองค์ประกอบด้วยเครื่องหมายจุด ( .) ในแผนภาพด้านล่าง:

เมทริกซ์ 2 × 2

. .
. .

เมทริกซ์ 3 × 3

. . .
. . .
. . .

เมทริกซ์ 4 × 4

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

ดังนั้นการหมุนเมทริกซ์หมายความว่าอย่างไร ลองเมทริกซ์ 2 × 2 และใส่จำนวนลงในแต่ละองค์ประกอบเพื่อให้สามารถสังเกตการหมุนได้:

0 1
2 3

การหมุนด้วย 90 องศาทำให้เรา:

2 0
3 1

เราเปลี่ยนเมทริกซ์ทั้งหมดไปทางขวาหนึ่งครั้งเหมือนกับการหมุนพวงมาลัยของรถยนต์ มันอาจช่วยให้คิดถึง“ การให้ทิป” เมทริกซ์ไปทางด้านขวาของมัน เราต้องการเขียนฟังก์ชันใน Python ที่ใช้เมทริกซ์และหมุนไปทางขวาหนึ่งครั้ง ลายเซ็นของฟังก์ชั่นจะเป็น:

def rotate(matrix):
    # Algorithm goes here.

เมทริกซ์จะถูกกำหนดโดยใช้อาร์เรย์สองมิติ:

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

ดังนั้นตำแหน่งดัชนีแรกเข้าถึงแถว ตำแหน่งดัชนีที่สองเข้าถึงคอลัมน์:

matrix[row][column]

เราจะกำหนดฟังก์ชั่นยูทิลิตี้เพื่อพิมพ์เมทริกซ์

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

วิธีหนึ่งในการหมุนเมทริกซ์คือการทำทีละชั้น แต่ชั้นคืออะไร คิดว่าหัวหอม เช่นเดียวกับเลเยอร์ของหัวหอมเนื่องจากแต่ละชั้นจะถูกลบออกเราย้ายไปที่กึ่งกลาง การเปรียบเทียบอื่น ๆ คือตุ๊กตา Matryoshkaหรือเกมส่งผ่านพัสดุ

ความกว้างและความสูงของเมทริกซ์กำหนดจำนวนของชั้นในเมทริกซ์นั้น มาใช้สัญลักษณ์ที่แตกต่างกันสำหรับแต่ละเลเยอร์:

เมทริกซ์ 2 × 2 มี 1 ชั้น

. .
. .

เมทริกซ์ 3 × 3 มี 2 ชั้น

. . .
. x .
. . .

เมทริกซ์ 4 × 4 มี 2 ชั้น

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

เมทริกซ์ขนาด 5 × 5 มี 3 ชั้น

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

เมทริกซ์ 6 × 6 มี 3 ชั้น

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

เมทริกซ์ขนาด 7 × 7 มี 4 ชั้น

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

คุณอาจสังเกตเห็นว่าการเพิ่มความกว้างและความสูงของเมทริกซ์ทีละหนึ่งจะไม่เพิ่มจำนวนเลเยอร์เสมอไป จากเมทริกซ์ด้านบนและการทำเป็นตารางเลเยอร์และมิติเราจะเห็นจำนวนเลเยอร์เพิ่มขึ้นหนึ่งครั้งสำหรับความกว้างและความสูงที่เพิ่มขึ้นทุกสองครั้ง:

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

อย่างไรก็ตามไม่ใช่ทุกชั้นที่ต้องหมุน เมทริกซ์ 1 × 1 จะเหมือนกันทั้งก่อนและหลังการหมุน เลเยอร์กลาง 1 × 1 นั้นจะเหมือนกันทั้งก่อนและหลังการหมุนเสมอไม่ว่าเมทริกซ์โดยรวมจะใหญ่แค่ไหน:

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

กำหนด N × N matrix เราจะกำหนดจำนวนชั้นที่ต้องการหมุนได้อย่างไร หากเราหารความกว้างหรือความสูงเป็นสองส่วนและไม่สนใจส่วนที่เหลือเราจะได้ผลลัพธ์ดังนี้

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

สังเกตว่าN/2จับคู่กับจำนวนเลเยอร์ที่ต้องหมุนได้อย่างไร บางครั้งจำนวนชั้นที่หมุนได้นั้นน้อยกว่าจำนวนชั้นทั้งหมดในเมทริกซ์ สิ่งนี้เกิดขึ้นเมื่อชั้นในสุดถูกสร้างขึ้นเพียงองค์ประกอบเดียว (เช่นเมทริกซ์ 1 × 1) ดังนั้นจึงไม่จำเป็นต้องหมุน มันก็จะถูกละเว้น

เราไม่ต้องสงสัยข้อมูลในฟังก์ชันของเราเพื่อหมุนเมทริกซ์ดังนั้นเรามาเพิ่มทันที:

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

ตอนนี้เรารู้ว่าเลเยอร์คืออะไรและจะกำหนดจำนวนเลเยอร์ที่ต้องการการหมุนได้อย่างไรเราจะแยกเลเยอร์เดียวเพื่อให้เราหมุนได้อย่างไร ประการแรกเราตรวจสอบเมทริกซ์จากชั้นนอกสุดไปจนถึงชั้นในสุด เมทริกซ์ 5 × 5 มีทั้งหมดสามชั้นและสองชั้นที่ต้องการการหมุน:

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

ลองดูที่คอลัมน์ก่อน ตำแหน่งของคอลัมน์ที่กำหนดเลเยอร์นอกสุดสมมติว่าเรานับจาก 0 คือ 0 และ 4:

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

0 และ 4 ยังเป็นตำแหน่งของแถวสำหรับเลเยอร์ชั้นนอกสุด

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

กรณีนี้จะเป็นเช่นนี้เสมอเนื่องจากความกว้างและความสูงเท่ากัน ดังนั้นเราสามารถกำหนดตำแหน่งคอลัมน์และแถวของเลเยอร์ด้วยค่าเพียงสองค่า (แทนที่จะเป็นสี่)

การย้ายเข้าสู่ชั้นที่สองตำแหน่งของคอลัมน์คือ 1 และ 3 และใช่คุณเดาได้ว่ามันเหมือนกันสำหรับแถว สิ่งสำคัญคือต้องเข้าใจว่าเราต้องเพิ่มและลดตำแหน่งแถวและคอลัมน์เมื่อเลื่อนเข้าสู่ชั้นถัดไป

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

ดังนั้นในการตรวจสอบแต่ละเลเยอร์เราต้องการลูปที่มีทั้งการเพิ่มและการลดเคาน์เตอร์ที่แสดงการเคลื่อนไหวเข้าด้านในโดยเริ่มจากเลเยอร์ชั้นนอกสุด เราจะเรียกสิ่งนี้ว่า 'layer loop' ของเรา

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

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

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

rotate(matrix)

โค้ดด้านบนวนลูปผ่านตำแหน่ง (แถวและคอลัมน์) ของเลเยอร์ใด ๆ ที่ต้องการการหมุน

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

ตอนนี้เรามีลูปที่แสดงตำแหน่งของแถวและคอลัมน์ของแต่ละเลเยอร์ ตัวแปรfirstและlastระบุตำแหน่งดัชนีของแถวและคอลัมน์แรกและสุดท้าย อ้างอิงกลับไปที่ตารางแถวและคอลัมน์ของเรา:

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

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

ดังนั้นเราสามารถนำทางผ่านชั้นของเมทริกซ์ ตอนนี้เราต้องการวิธีการนำทางภายในเลเยอร์เพื่อให้เราสามารถย้ายองค์ประกอบรอบเลเยอร์นั้น หมายเหตุองค์ประกอบจะไม่ 'กระโดด' จากเลเยอร์หนึ่งไปยังอีกเลเยอร์ แต่จะย้ายภายในเลเยอร์ที่เกี่ยวข้อง

การหมุนแต่ละองค์ประกอบในชั้นจะหมุนทั้งชั้น การหมุนเลเยอร์ทั้งหมดในเมทริกซ์จะหมุนเมทริกซ์ทั้งหมด ประโยคนี้มีความสำคัญมากดังนั้นโปรดพยายามอย่างดีที่สุดที่จะเข้าใจก่อนที่จะดำเนินการต่อไป

ตอนนี้เราต้องการวิธีในการเคลื่อนย้ายองค์ประกอบจริง ๆ เช่นหมุนแต่ละองค์ประกอบแล้วต่อมาเลเยอร์และในที่สุดเมทริกซ์ เพื่อความง่ายเราจะเปลี่ยนกลับเป็นเมทริกซ์ 3x3 ซึ่งมีชั้นหนึ่งที่หมุนได้

0 1 2
3 4 5
6 7 8

เลเยอร์เลเยอร์ของเรามีดัชนีของคอลัมน์แรกและคอลัมน์สุดท้ายรวมถึงแถวแรกและแถวสุดท้าย:

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

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

เนื่องจากเมทริกซ์ของเรามีกำลังสองเสมอเราต้องการเพียงสองตัวแปรfirstและlastเนื่องจากตำแหน่งดัชนีเหมือนกันสำหรับแถวและคอลัมน์

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

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

        first = layer
        last = size - first - 1

        # We want to move within a layer here.

ตัวแปรแรกและสุดท้ายสามารถใช้อ้างอิงมุมทั้งสี่ของเมทริกซ์ได้อย่างง่ายดาย นี่เป็นเพราะมุมตัวเองสามารถกำหนดได้โดยใช้วิธีเรียงสับเปลี่ยนของfirstและlast(โดยไม่มีการลบการบวกหรือชดเชยของตัวแปรเหล่านั้น):

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

ด้วยเหตุนี้เราจึงเริ่มการหมุนของเราที่มุมสี่ด้านนอก - เราจะหมุนมันก่อน *ขอเน้นพวกเขาด้วย

* 1 *
3 4 5
* 7 *

เราต้องการสลับแต่ละอัน*ด้วย*ทางด้านขวาของมัน งั้นเราจะพิมพ์มุมของเราโดยใช้พีชคณิตfirstและlast:

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

        first = layer
        last = size - first - 1

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

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

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

rotate(matrix)

ผลลัพธ์ควรเป็น:

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

ตอนนี้เราสามารถสลับมุมแต่ละมุมได้ง่ายจากภายในเลเยอร์ลูปของเรา:

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

        first = layer
        last = size - first - 1

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

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


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

เมทริกซ์ก่อนหมุนมุม:

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

เมทริกซ์หลังจากหมุนมุม:

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

ที่ดี! เราหมุนแต่ละมุมของเมทริกซ์ได้สำเร็จ แต่เราไม่ได้หมุนองค์ประกอบตรงกลางของแต่ละชั้น เห็นได้ชัดว่าเราต้องการวิธีการวนซ้ำภายในเลเยอร์

ปัญหาคือลูปเดียวในฟังก์ชันของเราจนถึง (เลเยอร์เลเยอร์ของเรา) ย้ายไปยังเลเยอร์ถัดไปในการวนซ้ำแต่ละครั้ง เนื่องจากเมทริกซ์ของเรามีเลเยอร์ที่หมุนได้เพียงหนึ่งชั้นเลเยอร์ออกจากหลังจากหมุนมุมเท่านั้น ลองดูว่าเกิดอะไรขึ้นกับเมทริกซ์ขนาดใหญ่ 5 × 5 (ที่สองชั้นต้องการหมุน) รหัสฟังก์ชั่นถูกละไว้ แต่มันยังคงเหมือนเดิม:

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

ผลลัพธ์คือ:

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

ไม่น่าแปลกใจที่มุมของชั้นนอกสุดได้รับการหมุน แต่คุณอาจสังเกตเห็นว่ามุมของชั้นถัดไป (ด้านใน) ถูกหมุนเช่นกัน มันสมเหตุสมผลแล้ว เราได้เขียนโค้ดเพื่อนำทางเลเยอร์และเพื่อหมุนมุมของแต่ละเลเยอร์ สิ่งนี้ให้ความรู้สึกก้าวหน้า แต่น่าเสียดายที่เราต้องถอยห่างออกไป มันไม่สามารถเคลื่อนที่ไปสู่เลเยอร์ถัดไปได้ดีจนกระทั่งเลเยอร์ก่อนหน้า (ด้านนอก) หมุนเต็มแล้ว นั่นคือจนกระทั่งแต่ละองค์ประกอบในเลเยอร์ถูกหมุน หมุนมุมเท่านั้นจะไม่ทำ!

หายใจลึก ๆ. เราต้องการวงอื่น วนซ้ำไม่น้อย วนซ้ำใหม่ที่ซ้อนกันจะใช้firstและlastตัวแปรรวมถึงออฟเซ็ตเพื่อนำทางภายในเลเยอร์ เราจะเรียกวงใหม่นี้ว่า 'วนรอบองค์ประกอบ' ห่วงองค์ประกอบจะเข้าสู่แต่ละองค์ประกอบตามแถวด้านบนแต่ละองค์ประกอบลงทางด้านขวาแต่ละองค์ประกอบตามแถวด้านล่างและแต่ละองค์ประกอบขึ้นทางด้านซ้าย

  • การย้ายไปข้างหน้าตามแถวด้านบนจะต้องมีการเพิ่มดัชนีคอลัมน์
  • การเลื่อนลงทางด้านขวาจะต้องมีการเพิ่มดัชนีแถว
  • การเลื่อนไปด้านหลังตามด้านล่างจะต้องทำให้ดัชนีคอลัมน์ลดลง
  • การเลื่อนขึ้นทางด้านซ้ายจะต้องมีการลดค่าดัชนีแถว

สิ่งนี้ฟังดูซับซ้อน แต่มันง่ายมากเพราะจำนวนครั้งที่เราเพิ่มและลดลงเพื่อให้ได้ตามข้างต้นยังคงเหมือนเดิมตลอดทั้งสี่ด้านของเมทริกซ์ ตัวอย่างเช่น:

  • ย้าย 1 องค์ประกอบข้ามแถวบนสุด
  • ย้ายองค์ประกอบ 1 ลงด้านขวา
  • ย้ายองค์ประกอบ 1 ไปข้างหลังตามแถวด้านล่าง
  • ย้าย 1 องค์ประกอบขึ้นไปทางซ้าย

ซึ่งหมายความว่าเราสามารถใช้ตัวแปรเดียวร่วมกับfirstและlastตัวแปรเพื่อย้ายภายในชั้น มันอาจช่วยให้ทราบว่าการย้ายข้ามแถวบนและด้านขวาลงทั้งสองต้องการการเพิ่ม ในขณะที่เคลื่อนที่ไปด้านหลังตามด้านล่างและด้านซ้ายทั้งคู่ต้องลดระดับลง

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

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

            first = layer
            last = size - first - 1

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

                offset = element - first

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

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

ตอนนี้เราเพียงแค่ต้องกำหนดด้านบนไปทางด้านขวาด้านขวาไปด้านล่างด้านล่างด้านซ้ายและด้านซ้ายไปด้านบน เรานำสิ่งนี้มารวมกัน:

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

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

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

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

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

รับเมทริกซ์:

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

rotateผลการทำงานของเราใน:

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

ตอนแรกฉันรู้สึกเหมือน "ว้าวคำอธิบายที่ดีที่สุดเท่าที่เคยมีมา" แต่หลังจากอ่านสองสามครั้ง (เพื่อให้แน่ใจว่าฉันไม่พลาดสิ่งสำคัญในทะเลคำ) ความคิดเห็นของฉันเปลี่ยนเป็น "คนฉันเข้าใจแล้ว เราต้องการให้มันเคลื่อนไหวได้ไหม " ยังคงถูกโหวตให้สละสิ่งที่ต้องใช้เวลาหลายชั่วโมงเพื่อเขียนคำตอบที่ซับซ้อน
Abhijit Sarkar

1
@AbhijitSarkar - ขอบคุณสำหรับการลงคะแนนและฉันหวังว่าอย่างน้อยก็ช่วยด้วยวิธีเล็ก ๆ น้อย ๆ แน่นอนคุณตอบถูกแล้วคำตอบของฉันคือพูดมาก อย่างไรก็ตามนี่เป็นการจงใจตรงกันข้ามกับคำตอบส่วนใหญ่ ดังที่ฉันพูดเมื่อเริ่มต้นคำตอบของฉัน: "ในคำตอบนี้แนวคิดที่สำคัญซ้ำแล้วซ้ำอีกก้าวช้าและซ้ำโดยเจตนา" หากคุณมีการแก้ไขที่รักษาความชัดเจนและความซ้ำซ้อนที่จำเป็น แต่ลดจำนวนคำฉันเปิดกว้างสำหรับคำแนะนำ หรือเพียงแค่แก้ไข :)
แจ็ค

@jack คำอธิบายที่ดีจริงๆ อย่างไรก็ตามฉันไม่สามารถยกเลิกการลากเส้นคุณคิดค่าชดเชยองค์ประกอบ = - เป็นครั้งแรกและครั้งสุดท้าย = ขนาด - ครั้งแรก - 1 ได้อย่างไร มีปัญหาในการทำความเข้าใจเรื่องนี้ไหม นอกจากนี้การชดเชยครั้งสุดท้ายเหมือนกับการชดเชย
ashishjmeshram

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

127

งูหลาม:

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

และทวนเข็มนาฬิกา:

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

มันทำงานอย่างไร:

zip(*original)จะสลับแกนของอาร์เรย์ 2d โดยการซ้อนรายการที่เกี่ยวข้องจากรายการเป็นรายการใหม่ ( *ผู้ประกอบการบอกฟังก์ชั่นเพื่อกระจายรายการที่มีอยู่ในข้อโต้แย้ง)

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

[::-1]องค์ประกอบคำสั่งหลี array (โปรดดูขยายชิ้นหรือคำถามนี้ ):

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

ในที่สุดการรวมสองสิ่งนี้จะส่งผลให้เกิดการเปลี่ยนแปลงการหมุน

การเปลี่ยนตำแหน่งของ[::-1]จะย้อนกลับรายการในระดับต่าง ๆ ของเมทริกซ์


3
ผมเชื่อว่านี่จะมาจากรหัสปีเตอร์นอร์วิก: norvig.com/python-iaq.html
ซิป

คุณสามารถใช้zip(*reversed(original))แทนที่จะzip(*original[::-1])หลีกเลี่ยงการสร้างสำเนาเพิ่มเติมของรายการต้นฉบับ
บอริส

70

นี่คือสิ่งที่หมุนได้แทนการใช้อาร์เรย์ใหม่ที่สมบูรณ์เพื่อเก็บผลลัพธ์ ฉันออกจากการเริ่มต้นของอาร์เรย์และพิมพ์ออกมา ใช้งานได้กับอาร์เรย์สี่เหลี่ยมเท่านั้น แต่สามารถมีขนาดใดก็ได้ ค่าใช้จ่ายหน่วยความจำเท่ากับขนาดขององค์ประกอบหนึ่งของอาร์เรย์เพื่อให้คุณสามารถทำการหมุนของอาร์เรย์ที่มีขนาดใหญ่เท่าที่คุณต้องการ

int a[4][4];
int n = 4;
int tmp;
for (int i = 0; i < n / 2; i++)
{
    for (int j = i; j < n - i - 1; j++)
    {
        tmp             = a[i][j];
        a[i][j]         = a[j][n-i-1];
        a[j][n-i-1]     = a[n-i-1][n-j-1];
        a[n-i-1][n-j-1] = a[n-j-1][i];
        a[n-j-1][i]     = tmp;
    }
}

ฉันเห็นข้อผิดพลาดอย่างน้อยหนึ่งข้อ หากคุณกำลังจะโพสต์รหัสทดสอบหรืออย่างน้อยก็บอกว่าคุณยังไม่ได้ทำ
ฮิวจ์อัลเลน

1
ที่ไหน? ชี้ให้เห็นแล้วฉันจะแก้ไข ฉันทดสอบและใช้งานได้ดีทั้งอาร์เรย์คี่และขนาดใหญ่
dagorym

2
มันเป็นทางออกที่สวยงาม จิตใจสามารถทำสิ่งนั้นได้ถ้าตั้งไว้ที่วัตถุประสงค์ จาก O (n2) ถึง O (1)
MoveFast

2
มันไม่ใช่ O (1); มันยังคงเป็น O (n ^ 2)
duma

11
มัน O (n ^ 2) กับหน่วยความจำ O (1)
Neel

38

มีโค้ดที่ดีมากมายที่นี่ แต่ฉันแค่ต้องการแสดงสิ่งที่เกิดขึ้นในเชิงเรขาคณิตเพื่อให้คุณสามารถเข้าใจตรรกะของโค้ดได้ดีขึ้นเล็กน้อย นี่คือวิธีที่ฉันจะเข้าใกล้นี้

ก่อนอื่นอย่าสับสนกับการขนย้ายซึ่งง่ายมาก ..

แนวคิด basica คือการปฏิบัติต่อมันเป็นเลเยอร์และเราหมุนทีละหนึ่งเลเยอร์ ..

บอกว่าเรามี 4x4

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

หลังจากเราหมุนตามเข็มนาฬิกา 90 เราจะได้

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

งั้นลองแยกนี่ก่อนอื่นเราหมุนมุมทั้งสี่กันก่อน

1           4


13          16

จากนั้นเราหมุนเพชรต่อไปนี้ซึ่งเป็นประเภทของความสงสัย

    2
            8
9       
        15

และจากนั้นเพชรที่สองเบ้

        3
5           
            12
    14

เพื่อที่จะดูแลขอบด้านนอกดังนั้นโดยพื้นฐานแล้วเราทำอย่างนั้นครั้งละเปลือก

ในที่สุดจัตุรัสกลาง (หรือถ้ามันแปลกเพียงองค์ประกอบสุดท้ายที่ไม่ย้าย)

6   7
10  11

ดังนั้นตอนนี้ลองหาดัชนีของแต่ละชั้นสมมติว่าเรามักจะทำงานกับชั้นนอกสุดที่เรากำลังทำอยู่

[0,0] -> [0,n-1], [0,n-1] -> [n-1,n-1], [n-1,n-1] -> [n-1,0], and [n-1,0] -> [0,0]
[0,1] -> [1,n-1], [1,n-2] -> [n-1,n-2], [n-1,n-2] -> [n-2,0], and [n-2,0] -> [0,1]
[0,2] -> [2,n-2], [2,n-2] -> [n-1,n-3], [n-1,n-3] -> [n-3,0], and [n-3,0] -> [0,2]

เรื่อย ๆ จนกว่าเราจะผ่านไปครึ่งทาง

ดังนั้นโดยทั่วไปรูปแบบคือ

[0,i] -> [i,n-i], [i,n-i] -> [n-1,n-(i+1)], [n-1,n-(i+1)] -> [n-(i+1),0], and [n-(i+1),0] to [0,i]

มันหมายถึงอะไร "ครึ่งทางผ่านขอบ"? ฉันเห็นอัลกอริทึมมากมายวนซ้ำจนถึง N / 2 และคนอื่น ๆ ที่วนลูปจนถึง N แต่ฉันไม่เห็นว่า N / 2 มาจากไหน
PDN

ฉันเชื่อว่ามันเป็นทางออกเดียวกับที่ให้ไว้ในการถอดรหัสการสัมภาษณ์ แต่ฉันชอบคำอธิบายทีละขั้นตอน ดีมากและทั่วถึง
Naphstor

@PDN คำตอบนี้อธิบายโดยละเอียด
Mathias Bynens

35

ดังที่ฉันได้กล่าวไปแล้วในบทความก่อนหน้านี้นี่คือโค้ดบางส่วนใน C # ที่ใช้การหมุนเมทริกซ์ O (1) สำหรับเมทริกซ์ขนาดใดก็ได้ เพื่อความกระชับและอ่านง่ายไม่มีการตรวจสอบข้อผิดพลาดหรือตรวจสอบช่วง รหัส:

static void Main (string [] args)
{
  int [,]
    //  create an arbitrary matrix
    m = {{0, 1}, {2, 3}, {4, 5}};

  Matrix
    //  create wrappers for the data
    m1 = new Matrix (m),
    m2 = new Matrix (m),
    m3 = new Matrix (m);

  //  rotate the matricies in various ways - all are O(1)
  m1.RotateClockwise90 ();
  m2.Rotate180 ();
  m3.RotateAnitclockwise90 ();

  //  output the result of transforms
  System.Diagnostics.Trace.WriteLine (m1.ToString ());
  System.Diagnostics.Trace.WriteLine (m2.ToString ());
  System.Diagnostics.Trace.WriteLine (m3.ToString ());
}

class Matrix
{
  enum Rotation
  {
    None,
    Clockwise90,
    Clockwise180,
    Clockwise270
  }

  public Matrix (int [,] matrix)
  {
    m_matrix = matrix;
    m_rotation = Rotation.None;
  }

  //  the transformation routines
  public void RotateClockwise90 ()
  {
    m_rotation = (Rotation) (((int) m_rotation + 1) & 3);
  }

  public void Rotate180 ()
  {
    m_rotation = (Rotation) (((int) m_rotation + 2) & 3);
  }

  public void RotateAnitclockwise90 ()
  {
    m_rotation = (Rotation) (((int) m_rotation + 3) & 3);
  }

  //  accessor property to make class look like a two dimensional array
  public int this [int row, int column]
  {
    get
    {
      int
        value = 0;

      switch (m_rotation)
      {
      case Rotation.None:
        value = m_matrix [row, column];
        break;

      case Rotation.Clockwise90:
        value = m_matrix [m_matrix.GetUpperBound (0) - column, row];
        break;

      case Rotation.Clockwise180:
        value = m_matrix [m_matrix.GetUpperBound (0) - row, m_matrix.GetUpperBound (1) - column];
        break;

      case Rotation.Clockwise270:
        value = m_matrix [column, m_matrix.GetUpperBound (1) - row];
        break;
      }

      return value;
    }

    set
    {
      switch (m_rotation)
      {
      case Rotation.None:
        m_matrix [row, column] = value;
        break;

      case Rotation.Clockwise90:
        m_matrix [m_matrix.GetUpperBound (0) - column, row] = value;
        break;

      case Rotation.Clockwise180:
        m_matrix [m_matrix.GetUpperBound (0) - row, m_matrix.GetUpperBound (1) - column] = value;
        break;

      case Rotation.Clockwise270:
        m_matrix [column, m_matrix.GetUpperBound (1) - row] = value;
        break;
      }
    }
  }

  //  creates a string with the matrix values
  public override string ToString ()
  {
    int
      num_rows = 0,
      num_columns = 0;

    switch (m_rotation)
    {
    case Rotation.None:
    case Rotation.Clockwise180:
      num_rows = m_matrix.GetUpperBound (0);
      num_columns = m_matrix.GetUpperBound (1);
      break;

    case Rotation.Clockwise90:
    case Rotation.Clockwise270:
      num_rows = m_matrix.GetUpperBound (1);
      num_columns = m_matrix.GetUpperBound (0);
      break;
    }

    StringBuilder
      output = new StringBuilder ();

    output.Append ("{");

    for (int row = 0 ; row <= num_rows ; ++row)
    {
      if (row != 0)
      {
        output.Append (", ");
      }

      output.Append ("{");

      for (int column = 0 ; column <= num_columns ; ++column)
      {
        if (column != 0)
        {
          output.Append (", ");
        }

        output.Append (this [row, column].ToString ());
      }

      output.Append ("}");
    }

    output.Append ("}");

    return output.ToString ();
  }

  int [,]
    //  the original matrix
    m_matrix;

  Rotation
    //  the current view of the matrix
    m_rotation;
}

ตกลงฉันจะยกมือขึ้นมันไม่ได้ทำการดัดแปลงใด ๆ กับอาเรย์ดั้งเดิมเมื่อหมุน แต่ในระบบ OO ที่ไม่สำคัญตราบใดที่วัตถุดูเหมือนว่ามันถูกหมุนไปยังลูกค้าของชั้นเรียน ในขณะนี้คลาส Matrix ใช้การอ้างอิงไปยังข้อมูลอาเรย์ดั้งเดิมดังนั้นการเปลี่ยนแปลงค่าใด ๆ ของ m1 จะเปลี่ยน m2 และ m3 การเปลี่ยนแปลงเล็กน้อยในตัวสร้างเพื่อสร้างอาร์เรย์ใหม่และคัดลอกค่าไปยังมันจะเรียงลำดับที่ออก


4
ไชโย! นี่เป็นทางออกที่ดีมากและฉันไม่รู้ว่าทำไมมันถึงไม่ใช่คำตอบที่ได้รับการยอมรับ
martinatime

@martinatime: อาจเป็นเพราะมันใหญ่กว่า 5 เท่า
คางคก

@ โหลด: การเขียนโค้ดมักเป็นการแลกเปลี่ยนระหว่างความต้องการของคู่แข่ง: ความเร็วขนาดราคาและอื่น ๆ
Skizz

15
จริง ... ปัญหาอีกข้อคือความจริงที่ว่าเมทริกซ์ไม่ได้หมุน แต่หมุนในเวลาพอดี ซึ่งดีมากสำหรับการเข้าถึงองค์ประกอบบางอย่าง แต่น่ากลัวถ้าใช้เมทริกซ์นี้ในการคำนวณหรือการปรับแต่งภาพ ดังนั้นการบอกว่า O (1) ไม่ยุติธรรมเลย
คางคก

23

ในขณะที่การหมุนข้อมูลอาจจำเป็น

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

ถ้าคุณMatrixใช้อินเทอร์เฟซนี้อยู่แล้วมันสามารถหมุนผ่านคลาสมัณฑนากรเช่นนี้:

class RotatedMatrix : IReadableMatrix
{
    private readonly IReadableMatrix _baseMatrix;

    public RotatedMatrix(IReadableMatrix baseMatrix)
    {
        _baseMatrix = baseMatrix;
    }

    int GetValue(int x, int y)
    {
        // transpose x and y dimensions
        return _baseMatrix(y, x);
    }
}

การหมุน + 90 / -90 / 180 องศาการพลิกในแนวนอน / แนวตั้งและการปรับสเกลสามารถทำได้ในแบบนี้เช่นกัน

จะต้องมีการวัดประสิทธิภาพในสถานการณ์เฉพาะของคุณ อย่างไรก็ตามการดำเนินการ O (n ^ 2) ได้ถูกแทนที่ด้วยการโทร O (1) มันเป็นวิธีการเรียกเสมือนซึ่งเป็นช้ากว่าการเข้าถึงอาร์เรย์โดยตรงดังนั้นจึงขึ้นอยู่กับความถี่อาร์เรย์หมุนใช้หลังจากการหมุน หากใช้เพียงครั้งเดียววิธีการนี้จะชนะแน่นอน ถ้ามันหมุนแล้วใช้ในระบบที่ใช้เวลานานหลายวันการหมุนแบบแทนที่อาจทำงานได้ดีขึ้น นอกจากนี้ยังขึ้นอยู่กับว่าคุณสามารถรับต้นทุนล่วงหน้าได้หรือไม่

เช่นเดียวกับปัญหาด้านประสิทธิภาพการวัดการวัดการวัด!


1
1 ... และถ้าเมทริกซ์ที่มีขนาดใหญ่มากและคุณเท่านั้นที่เข้าถึงองค์ประกอบคู่ (ใช้เบาบาง) มันมีประสิทธิภาพมากยิ่งขึ้น
Lothar

16
ดูเหมือนจะไม่ยุติธรรมเล็กน้อยที่จะเรียกนี่เป็นวิธีแก้ปัญหาเวลา O (1) เพื่อแก้ปัญหาที่เกิดจาก OP นี้จะยังคงใช้เวลา O (n ^ 2) ไม่เพียงแค่นั้นมันจะไม่แก้ปัญหาเพราะมันจะส่งคืนทรานสโพส ตัวอย่างที่ให้มาไม่มีการแปลงเป็นโซลูชัน
Vlad the Impala

5
ทีนี้ถ้าสิ่งที่คุณต้องการคือ 3 องค์ประกอบแรกของเมทริกซ์นี่เป็นคำตอบที่ดี แต่ปัญหาคือการดึงเมทริกซ์ที่ถูกเปลี่ยนรูปทั้งหมด (เช่นสมมติว่าคุณต้องการองค์ประกอบเมทริกซ์ทั้งหมด ) การเรียก O (1) นี้เป็นวิธีการแลกเปลี่ยนเครดิตเริ่มต้นของการวิเคราะห์อัลกอริทึม - คุณยังไม่ได้แก้ปัญหาคุณเพิ่งผลักมันให้คนอื่น :)
Ana Betts

4
@ พอลเบตต์: ฉันได้รับจุดของคุณ แต่เหมือนที่ฉันเขียนไว้ข้างต้นในความคิดเห็นแม้ว่าคุณจะมีเมทริกซ์ transposed จริงคุณยังคงต้องเขียนวงถ้าคุณต้องการที่จะอ่านค่าออกมา ดังนั้นการอ่านค่าทั้งหมดจากเมทริกซ์จึงเป็น O (N ^ 2) เสมอ ความแตกต่างที่นี่คือถ้าคุณทรานสปินสเกลสเกลอีกครั้ง ฯลฯ จากนั้นคุณจะยังคงกดปุ่ม O (N ^ 2) ครั้งเดียวเท่านั้น อย่างที่ฉันพูดไปมันไม่ได้เป็นทางออกที่ดีที่สุดเสมอไป แต่ในหลาย ๆ กรณีมันเหมาะสมและคุ้มค่า OP ดูเหมือนจะกำลังมองหาทางออกเวทมนตร์และนี่ก็ใกล้เคียงกับที่คุณจะได้รับ
Drew Noakes

9
ฉันชอบคำตอบนี้ แต่ฉันต้องการชี้บางสิ่ง การพิมพ์เมทริกซ์ที่มีการตกแต่ง (และการอ่านตามลำดับโดยทั่วไป) อาจช้ากว่าการทำเช่นเดียวกันกับเมทริกซ์ที่หมุนในหน่วยความจำและไม่ใช่เพียงเพราะการเรียกใช้เมธอดเสมือน สำหรับเมทริกซ์ตัวใหญ่คุณจะเพิ่มจำนวนแคชที่คุณพลาดไปโดยการอ่าน "ลง" มากกว่า "ข้าม"
Mike Daniels

18

นี่เป็นเวอร์ชั่นที่ดีกว่าใน Java: ฉันสร้างมันเป็นเมทริกซ์ที่มีความกว้างและความสูงต่างกัน

  • h คือความสูงของเมทริกซ์หลังจากการหมุน
  • w นี่คือความกว้างของเมทริกซ์หลังจากหมุน

 

public int[][] rotateMatrixRight(int[][] matrix)
{
    /* W and H are already swapped */
    int w = matrix.length;
    int h = matrix[0].length;
    int[][] ret = new int[h][w];
    for (int i = 0; i < h; ++i) {
        for (int j = 0; j < w; ++j) {
            ret[i][j] = matrix[w - j - 1][i];
        }
    }
    return ret;
}


public int[][] rotateMatrixLeft(int[][] matrix)
{
    /* W and H are already swapped */
    int w = matrix.length;
    int h = matrix[0].length;   
    int[][] ret = new int[h][w];
    for (int i = 0; i < h; ++i) {
        for (int j = 0; j < w; ++j) {
            ret[i][j] = matrix[j][h - i - 1];
        }
    }
    return ret;
}

รหัสนี้เป็นไปตามการโพสต์ของ Nick Berardi


ขอบคุณ นี่คือรหัส Java ที่ชัดเจนที่สุดที่นี่ คำถาม - คุณ / นิคมาเป็นอย่างไรกับส่วน [w - j - 1] ดูคำตอบ @tweaking ฉันสามารถดูว่าคุณจะได้รับจากการเหนี่ยวนำ / ตัวอย่างการแก้ เพียงแค่สงสัยว่าเป็นวิธีที่ได้มาหรือไม่ก็ขึ้นอยู่กับหลักการทางคณิตศาสตร์บางอย่างที่เกี่ยวข้องกับเมทริกซ์
Quest Monger

17

ทับทิมทาง: .transpose.map &:reverse


1
มันง่ายกว่านั้นอีก: array.reverse.transposeหมุนอาร์เรย์ตามเข็มนาฬิกาในขณะที่array.transpose.reverseหมุนทวนเข็มนาฬิกา mapไม่จำเป็นต้องเป็น
Giorgi Gzirishvili

13

มีคำตอบมากมายอยู่แล้วและฉันพบว่าทั้งสองอ้างสิทธิ์ความซับซ้อนของเวลา O (1) จริง O (1) ขั้นตอนวิธีการคือการออกจัดเก็บอาร์เรย์แตะต้องและเปลี่ยนวิธีที่คุณดัชนีองค์ประกอบของ เป้าหมายที่นี่คือมันไม่ใช้หน่วยความจำเพิ่มเติมและไม่ต้องใช้เวลาเพิ่มเติมในการย้ำข้อมูล

การหมุน 90, -90 และ 180 องศาเป็นการแปลงอย่างง่ายซึ่งสามารถดำเนินการได้ตราบใดที่คุณรู้ว่ามีกี่แถวและคอลัมน์ในอาร์เรย์ 2 มิติของคุณ ในการหมุนเวกเตอร์ใด ๆ 90 องศาให้สลับแกนและทำให้แกน Y เป็นโมฆะ สำหรับ -90 องศาให้สลับแกนและลบล้างแกน X สำหรับ 180 องศาลบล้างแกนทั้งสองโดยไม่มีการแลกเปลี่ยน

การแปลงเพิ่มเติมสามารถทำได้เช่นการทำมิเรอร์ในแนวนอนและ / หรือแนวตั้ง

ซึ่งสามารถทำได้ผ่านเช่นวิธีการเข้าถึง ตัวอย่างด้านล่างคือฟังก์ชั่น JavaScript แต่แนวคิดนี้มีผลบังคับใช้กับทุกภาษาอย่างเท่าเทียมกัน

 // Get an array element in column/row order
 var getArray2d = function(a, x, y) {
   return a[y][x];
 };

 //demo
 var arr = [
   [5, 4, 6],
   [1, 7, 9],
   [-2, 11, 0],
   [8, 21, -3],
   [3, -1, 2]
 ];

 var newarr = [];
 arr[0].forEach(() => newarr.push(new Array(arr.length)));

 for (var i = 0; i < newarr.length; i++) {
   for (var j = 0; j < newarr[0].length; j++) {
     newarr[i][j] = getArray2d(arr, i, j);
   }
 }
 console.log(newarr);

// Get an array element rotated 90 degrees clockwise
function getArray2dCW(a, x, y) {
  var t = x;
  x = y;
  y = a.length - t - 1;
  return a[y][x];
}

//demo
var arr = [
  [5, 4, 6],
  [1, 7, 9],
  [-2, 11, 0],
  [8, 21, -3],
  [3, -1, 2]
];

var newarr = [];
arr[0].forEach(() => newarr.push(new Array(arr.length)));

for (var i = 0; i < newarr[0].length; i++) {
  for (var j = 0; j < newarr.length; j++) {
    newarr[j][i] = getArray2dCW(arr, i, j);
  }
}
console.log(newarr);

// Get an array element rotated 90 degrees counter-clockwise
function getArray2dCCW(a, x, y) {
  var t = x;
  x = a[0].length - y - 1;
  y = t;
  return a[y][x];
}

//demo
var arr = [
  [5, 4, 6],
  [1, 7, 9],
  [-2, 11, 0],
  [8, 21, -3],
  [3, -1, 2]
];

var newarr = [];
arr[0].forEach(() => newarr.push(new Array(arr.length)));

for (var i = 0; i < newarr[0].length; i++) {
  for (var j = 0; j < newarr.length; j++) {
    newarr[j][i] = getArray2dCCW(arr, i, j);
  }
}
console.log(newarr);

// Get an array element rotated 180 degrees
function getArray2d180(a, x, y) {
  x = a[0].length - x - 1;
  y = a.length - y - 1;
  return a[y][x];
}

//demo
var arr = [
  [5, 4, 6],
  [1, 7, 9],
  [-2, 11, 0],
  [8, 21, -3],
  [3, -1, 2]
];

var newarr = [];
arr.forEach(() => newarr.push(new Array(arr[0].length)));

for (var i = 0; i < newarr[0].length; i++) {
  for (var j = 0; j < newarr.length; j++) {
    newarr[j][i] = getArray2d180(arr, i, j);
  }
}
console.log(newarr);

รหัสนี้จะถือว่าอาร์เรย์ของอาร์เรย์ที่ซ้อนกันโดยที่แต่ละอาร์เรย์ภายในเป็นแถว

วิธีการช่วยให้คุณสามารถอ่าน (หรือเขียน) องค์ประกอบ (แม้ในลำดับสุ่ม) ราวกับว่าอาร์เรย์ได้รับการหมุนหรือเปลี่ยน ตอนนี้เพียงแค่เลือกฟังก์ชั่นที่เหมาะสมในการโทรโดยอ้างอิงและคุณจะไป!

แนวคิดสามารถขยายเพื่อใช้การแปลงเสริม (และไม่ทำลาย) ผ่านวิธีการเข้าถึง รวมถึงการหมุนมุมโดยพลการและการปรับขนาด


ไม่มีสิ่งเหล่านี้หมุนจริงจากอาร์เรย์เดิมแม้ว่า คนแรกผลลัพธ์ที่ได้คือการย้ายเพียงแค่ อย่างที่สองคุณดูเหมือนจะสับแถวหรือมิเรอร์ข้ามกึ่งกลางแนวนอน ที่สามคุณย้อนกลับแถวและที่สี่คือ transposed ไม่มีใคร "หมุน" จริง ๆ
SM177Y

มีข้อบกพร่องบางอย่างในสองตัวอย่างหลัง แก้ไขเล็กน้อย ฉันชี้ให้เห็นอย่างชัดเจนว่าวิธีนี้ไม่ใช่การหมุนในตำแหน่ง มันเป็นฟังก์ชั่นการแปลงซึ่งทำให้มันเหมาะสำหรับการทำซ้ำขี้เกียจ
Jason Oster

ยกเว้นว่าไม่มีการหมุนดังนั้นคุณจึงไม่ตอบสิ่งที่ OP ถามจริง ๆ
SM177Y

@ SM177Y เครื่องมือแก้ไขอื่นได้เพิ่มโค้ดตัวอย่างที่ไม่ทำงานให้กับคำตอบของฉัน ฉันสามารถดูว่าคุณสับสนอย่างไร ฉันได้แก้ไขข้อบกพร่องในลูปการวนซ้ำแล้ว ฟังก์ชั่นตามที่ระบุไว้ทำในความเป็นจริง "หมุน" ข้อมูลในอาร์เรย์
Jason Oster

รายละเอียดที่สำคัญก็คือรหัสตัวอย่างจะล้างคำตอบดั้งเดิมที่ฉันให้ไว้ซึ่งพยายามแสดงให้เห็นถึงพลังของการแปลงหน้าที่การทำงานผ่านการแก้ปัญหาความซับซ้อนเชิงเส้น - เวลาเชิงพื้นที่ ด้วยการแปลงสภาพการทำงานคุณจะวนซ้ำหรือเข้าถึงองค์ประกอบอาร์เรย์อยู่แล้วดังนั้นการแปลงจึงถือว่า "ว่าง" ในแง่ของความคงที่และเวลาที่ซับซ้อน
Jason Oster

10

มีคนสองคนได้วางตัวอย่างซึ่งเกี่ยวข้องกับการสร้างอาร์เรย์ใหม่

สิ่งอื่น ๆ ที่ควรพิจารณา:

(a) แทนที่จะย้ายข้อมูลจริงเพียงแค่สำรวจอาร์เรย์ "หมุน" แตกต่างกัน

(b) การหมุนภาพในตำแหน่งอาจทำได้ยากกว่า คุณจะต้องมีรอยขีดข่วนเล็กน้อย (อาจประมาณเท่ากับหนึ่งแถวหรือขนาดคอลัมน์) มีกระดาษ ACM โบราณเกี่ยวกับการทำหน้าที่สับเปลี่ยนตำแหน่ง ( http://doi.acm.org/10.1145/355719.355729 ) แต่โค้ดตัวอย่างของพวกเขาน่ารังเกียจ goto-laden FORTRAN

ภาคผนวก:

http://doi.acm.org/10.1145/355611.355612เป็นอีกหนึ่งอัลกอริทึมการโอนย้ายแบบแทนที่ตามที่คาดคะเน


ฉันเห็นด้วยกับสิ่งนี้ มีวิธีการที่กำหนดการแปลระหว่างข้อมูลต้นฉบับและข้อมูล "หมุน"
martinatime

8

คำตอบของ Nickจะใช้ได้กับอาเรย์ NxM ด้วยเช่นกันโดยมีการดัดแปลงเพียงเล็กน้อย (เมื่อเทียบกับ NxN)

string[,] orig = new string[n, m];
string[,] rot = new string[m, n];

...

for ( int i=0; i < n; i++ )
  for ( int j=0; j < m; j++ )
    rot[j, n - i - 1] = orig[i, j];

วิธีหนึ่งที่จะคิดเกี่ยวกับสิ่งนี้คือคุณได้ย้ายจุดศูนย์กลางของแกน (0,0) จากมุมบนซ้ายไปยังมุมขวาบน คุณเพียงแค่เปลี่ยนจากคนหนึ่งไปสู่อีกคนหนึ่ง


6

เวลา - O (N), ช่องว่าง - O (1)

public void rotate(int[][] matrix) {
    int n = matrix.length;
    for (int i = 0; i < n / 2; i++) {
        int last = n - 1 - i;
        for (int j = i; j < last; j++) {
            int top = matrix[i][j];
            matrix[i][j] = matrix[last - j][i];
            matrix[last - j][i] = matrix[last][last - j];
            matrix[last][last - j] = matrix[j][last];
            matrix[j][last] = top;
        }
    }
}

นี่ไม่ใช่ O (1) นี่คือ O (n)
Jason Oster

@ JasonOster ฉันเชื่อว่านี่เป็นพื้นที่ O (1) เนื่องจากไม่มีการใช้พื้นที่เพิ่มเติม
ffledgling

@ffledgling ความผิดพลาดของฉัน O (1) ความซับซ้อนของพื้นที่ใช่ ความซับซ้อนของเวลา O (n)
Jason Oster

ความซับซ้อนของอวกาศคือ O (n) เช่นกัน ความซับซ้อนของพื้นที่ควรรวมถึงพื้นที่ของขนาดตัวแปรอินพุต careercup.com/question?id=14952322
Jason Heo

ฉันจะแก้ไขสิ่งนี้ให้ทำงานสำหรับการหมุนทวนเข็มนาฬิกาได้อย่างไร
MD XF

5

นี่คือรุ่น Ruby ของฉัน (โปรดสังเกตว่าค่าจะไม่แสดงเหมือนเดิม แต่ยังคงหมุนตามที่อธิบายไว้)

def rotate(matrix)
  result = []
  4.times { |x|
    result[x] = []
    4.times { |y|
      result[x][y] = matrix[y][3 - x]
    }
  }

  result
end

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

def print_matrix(matrix)
  4.times { |y|
    4.times { |x|
      print "#{matrix[x][y]} "
    }
    puts ""
  }
end

print_matrix(matrix)
puts ""
print_matrix(rotate(matrix))

ผลลัพธ์:

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

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

4

นี่คือวิธีการหมุนในช่องว่างโดยจาวาสำหรับสี่เหลี่ยมเท่านั้น สำหรับอาร์เรย์ 2d ที่ไม่ใช่สแควร์คุณจะต้องสร้างอาร์เรย์ใหม่

private void rotateInSpace(int[][] arr) {
    int z = arr.length;
    for (int i = 0; i < z / 2; i++) {
        for (int j = 0; j < (z / 2 + z % 2); j++) {
            int x = i, y = j;
            int temp = arr[x][y];
            for (int k = 0; k < 4; k++) {
                int temptemp = arr[y][z - x - 1];
                arr[y][z - x - 1] = temp;
                temp = temptemp;

                int tempX = y;
                y = z - x - 1;
                x = tempX;
            }
        }
    }
}

รหัสเพื่อหมุนอาร์เรย์ 2 มิติทุกขนาดโดยการสร้างอาร์เรย์ใหม่:

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

การนำไปใช้ +90 pseudocode ของลักยิ้ม (เช่นการแปลงแล้วย้อนกลับแต่ละแถว) ใน JavaScript:

function rotate90(a){
  // transpose from http://www.codesuck.com/2012/02/transpose-javascript-array-in-one-line.html
  a = Object.keys(a[0]).map(function (c) { return a.map(function (r) { return r[c]; }); });
  // row reverse
  for (i in a){
    a[i] = a[i].reverse();
  }
  return a;
}

3

คุณสามารถทำได้ใน3 ขั้นตอนง่ายๆ :

1 ) สมมติว่าเรามีเมทริกซ์

   1 2 3
   4 5 6
   7 8 9

2 ) นำทรานสดิวเซอร์ของเมทริกซ์

   1 4 7
   2 5 8
   3 6 9

3 ) การแลกเปลี่ยนแถวเพื่อรับเมทริกซ์หมุน

   3 6 9
   2 5 8
   1 4 7

ซอร์สโค้ดJavaสำหรับสิ่งนี้:

public class MyClass {

    public static void main(String args[]) {
        Demo obj = new Demo();
        /*initial matrix to rotate*/
        int[][] matrix = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
        int[][] transpose = new int[3][3]; // matrix to store transpose

        obj.display(matrix);              // initial matrix

        obj.rotate(matrix, transpose);    // call rotate method
        System.out.println();
        obj.display(transpose);           // display the rotated matix
    }
}

class Demo {   
    public void rotate(int[][] mat, int[][] tran) {

        /* First take the transpose of the matrix */
        for (int i = 0; i < mat.length; i++) {
            for (int j = 0; j < mat.length; j++) {
                tran[i][j] = mat[j][i]; 
            }
        }

        /*
         * Interchange the rows of the transpose matrix to get rotated
         * matrix
         */
        for (int i = 0, j = tran.length - 1; i != j; i++, j--) {
            for (int k = 0; k < tran.length; k++) {
                swap(i, k, j, k, tran);
            }
        }
    }

    public void swap(int a, int b, int c, int d, int[][] arr) {
        int temp = arr[a][b];
        arr[a][b] = arr[c][d];
        arr[c][d] = temp;    
    }

    /* Method to display the matrix */
    public void display(int[][] arr) {
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length; j++) {
                System.out.print(arr[i][j] + " ");
            }
            System.out.println();
        }
    }
}

เอาท์พุท:

1 2 3 
4 5 6 
7 8 9 

3 6 9 
2 5 8 
1 4 7 

2

นี่คือการนำไปใช้ของฉันใน C, O (1) ความซับซ้อนของหน่วยความจำในการหมุนสถานที่ 90 องศาตามเข็มนาฬิกา:

#include <stdio.h>

#define M_SIZE 5

static void initMatrix();
static void printMatrix();
static void rotateMatrix();

static int m[M_SIZE][M_SIZE];

int main(void){
    initMatrix();
    printMatrix();
    rotateMatrix();
    printMatrix();

    return 0;
}

static void initMatrix(){
    int i, j;

    for(i = 0; i < M_SIZE; i++){
        for(j = 0; j < M_SIZE; j++){
            m[i][j] = M_SIZE*i + j + 1;
        }
    }
}

static void printMatrix(){
    int i, j;

    printf("Matrix\n");
    for(i = 0; i < M_SIZE; i++){
        for(j = 0; j < M_SIZE; j++){
            printf("%02d ", m[i][j]);
        }
        printf("\n");
    }
    printf("\n");
}

static void rotateMatrix(){
    int r, c;

    for(r = 0; r < M_SIZE/2; r++){
        for(c = r; c < M_SIZE - r - 1; c++){
            int tmp = m[r][c];

            m[r][c] = m[M_SIZE - c - 1][r];
            m[M_SIZE - c - 1][r] = m[M_SIZE - r - 1][M_SIZE - c - 1];
            m[M_SIZE - r - 1][M_SIZE - c - 1] = m[c][M_SIZE - r - 1];
            m[c][M_SIZE - r - 1] = tmp;
        }
    }
}

2

นี่คือเวอร์ชั่น Java:

public static void rightRotate(int[][] matrix, int n) {
    for (int layer = 0; layer < n / 2; layer++) {
        int first = layer;
        int last = n - 1 - first;
        for (int i = first; i < last; i++) {
           int offset = i - first;
           int temp = matrix[first][i];
           matrix[first][i] = matrix[last-offset][first];
           matrix[last-offset][first] = matrix[last][last-offset];
           matrix[last][last-offset] = matrix[i][last];
           matrix[i][last] = temp;
        }
    }
}

วิธีแรกหมุนชั้นนอกสุดแล้วเลื่อนไปที่ชั้นในตามลำดับ


2

จากมุมมองเชิงเส้นให้พิจารณาเมทริกซ์:

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

ทีนี้ลองเปลี่ยนเป็น A

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

และพิจารณาการกระทำของ A 'on B หรือ B on A'
ตามลำดับ:

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

นี่คือขยายได้สำหรับเมทริกซ์ nxn ใด ๆ และใช้แนวคิดนี้อย่างรวดเร็วในรหัส:

void swapInSpace(int** mat, int r1, int c1, int r2, int c2)
{
    mat[r1][c1] ^= mat[r2][c2];
    mat[r2][c2] ^= mat[r1][c1];
    mat[r1][c1] ^= mat[r2][c2];
}

void transpose(int** mat, int size)
{
    for (int i = 0; i < size; i++)
    {
        for (int j = (i + 1); j < size; j++)
        {
            swapInSpace(mat, i, j, j, i);
        }
    }
}

void rotate(int** mat, int size)
{
    //Get transpose
    transpose(mat, size);

    //Swap columns
    for (int i = 0; i < size / 2; i++)
    {
        for (int j = 0; j < size; j++)
        {
            swapInSpace(mat, i, j, size - (i + 1), j);
        }
    }
}

2

รหัส C # เพื่อหมุน [n, m] อาร์เรย์ 2 มิติ 90 องศาทางขวา

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MatrixProject
{
    // mattrix class

    class Matrix{
        private int rows;
        private int cols;
        private int[,] matrix;

        public Matrix(int n){
            this.rows = n;
            this.cols = n;
            this.matrix = new int[this.rows,this.cols];

        }

        public Matrix(int n,int m){
            this.rows = n;
            this.cols = m;

            this.matrix = new int[this.rows,this.cols];
        }

        public void Show()
        {
            for (var i = 0; i < this.rows; i++)
            {
                for (var j = 0; j < this.cols; j++) {
                    Console.Write("{0,3}", this.matrix[i, j]);
                }
                Console.WriteLine();
            }                
        }

        public void ReadElements()
        {
           for (var i = 0; i < this.rows; i++)
                for (var j = 0; j < this.cols; j++)
                {
                    Console.Write("element[{0},{1}]=",i,j);
                    this.matrix[i, j] = Convert.ToInt32(Console.ReadLine());
                }            
        }


        // rotate [n,m] 2D array by 90 deg right
        public void Rotate90DegRight()
        {

            // create a mirror of current matrix
            int[,] mirror = this.matrix;

            // create a new matrix
            this.matrix = new int[this.cols, this.rows];

            for (int i = 0; i < this.rows; i++)
            {
                for (int j = 0; j < this.cols; j++)
                {
                    this.matrix[j, this.rows - i - 1] = mirror[i, j];
                }
            }

            // replace cols count with rows count
            int tmp = this.rows;
            this.rows = this.cols;
            this.cols = tmp;           
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Matrix myMatrix = new Matrix(3,4);
            Console.WriteLine("Enter matrix elements:");
            myMatrix.ReadElements();
            Console.WriteLine("Matrix elements are:");
            myMatrix.Show();
            myMatrix.Rotate90DegRight();
            Console.WriteLine("Matrix rotated at 90 deg are:");
            myMatrix.Show();
            Console.ReadLine();
        }
    }
}

ผลลัพธ์:

    Enter matrix elements:
    element[0,0]=1
    element[0,1]=2
    element[0,2]=3
    element[0,3]=4
    element[1,0]=5
    element[1,1]=6
    element[1,2]=7
    element[1,3]=8
    element[2,0]=9
    element[2,1]=10
    element[2,2]=11
    element[2,3]=12
    Matrix elements are:
      1  2  3  4
      5  6  7  8
      9 10 11 12
    Matrix rotated at 90 deg are:
      9  5  1
     10  6  2
     11  7  3
     12  8  4

2

PHP:

<?php    
$a = array(array(1,2,3,4),array(5,6,7,8),array(9,0,1,2),array(3,4,5,6));
$b = array(); //result

while(count($a)>0)
{
    $b[count($a[0])-1][] = array_shift($a[0]);
    if (count($a[0])==0)
    {
         array_shift($a);
    }
}

จาก PHP5.6 การขนย้าย Array สามารถทำได้ด้วยการarray_map()โทรที่มีประสิทธิภาพ กล่าวอีกนัยหนึ่งคอลัมน์จะถูกแปลงเป็นแถว

รหัส: ( สาธิต )

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

$ ย้าย:

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


1

#transpose เป็นวิธีมาตรฐานของคลาส Array ของ Ruby ดังนั้น:

% irb
irb(main):001:0> m = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 0, 1, 2], [3, 4, 5, 6]]
=> [[1, 2, 3, 4], [5, 6, 7, 8], [9, 0, 1, 2], [3, 4, 5, 6]] 
irb(main):002:0> m.reverse.transpose
=> [[3, 9, 5, 1], [4, 0, 6, 2], [5, 1, 7, 3], [6, 2, 8, 4]]

การใช้งานเป็นฟังก์ชั่นการขนย้าย n ^ 2 ที่เขียนใน C. คุณสามารถดูได้ที่นี่: http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-transpose โดยเลือก "คลิก เพื่อสลับแหล่งที่มา "ข้าง" transpose "

ฉันจำได้ดีกว่าโซลูชัน O (n ^ 2) แต่สำหรับเมทริกซ์ที่สร้างขึ้นเป็นพิเศษเท่านั้น (เช่นเมทริกซ์เบาบาง)


1

รหัส C สำหรับการหมุนเมทริกซ์ 90 องศาตามเข็มนาฬิกา IN PLACE สำหรับเมทริกซ์ M * N ใด ๆ

void rotateInPlace(int * arr[size][size], int row, int column){
    int i, j;
    int temp = row>column?row:column;
    int flipTill = row < column ? row : column;
    for(i=0;i<flipTill;i++){
        for(j=0;j<i;j++){
            swapArrayElements(arr, i, j);
        }
    }

    temp = j+1;

    for(i = row>column?i:0; i<row; i++){
            for(j=row<column?temp:0; j<column; j++){
                swapArrayElements(arr, i, j);
            }
    }

    for(i=0;i<column;i++){
        for(j=0;j<row/2;j++){
            temp = arr[i][j];
            arr[i][j] = arr[i][row-j-1];
            arr[i][row-j-1] = temp;
        }
    }
}

1

นี่คือการดำเนินการของฉันใน C

void rotateRight(int matrix[][SIZE], int length) {

    int layer = 0;

    for (int layer = 0; layer < length / 2; ++layer) {

        int first = layer;
        int last = length - 1 - layer;

        for (int i = first; i < last; ++i) {

            int topline = matrix[first][i];
            int rightcol = matrix[i][last];
            int bottomline = matrix[last][length - layer - 1 - i];
            int leftcol = matrix[length - layer - 1 - i][first];

            matrix[first][i] = leftcol;
            matrix[i][last] = topline;
            matrix[last][length - layer - 1 - i] = rightcol;
            matrix[length - layer - 1 - i][first] = bottomline;
        }
    }
}

1

นี่คือความพยายามของฉันสำหรับการหมุนเมทริกซ์ 90 องศาซึ่งเป็นวิธีแก้ปัญหา 2 ขั้นตอนในซีขั้นแรกให้เปลี่ยนเมทริกซ์ให้เข้าที่จากนั้นสลับคอล

#define ROWS        5
#define COLS        5

void print_matrix_b(int B[][COLS], int rows, int cols) 
{
    for (int i = 0; i <= rows; i++) {
        for (int j = 0; j <=cols; j++) {
            printf("%d ", B[i][j]);
        }
        printf("\n");
    }
}

void swap_columns(int B[][COLS], int l, int r, int rows)
{
    int tmp;
    for (int i = 0; i <= rows; i++) {
        tmp = B[i][l];
        B[i][l] = B[i][r];
        B[i][r] = tmp;
    }
}


void matrix_2d_rotation(int B[][COLS], int rows, int cols)
{
    int tmp;
    // Transpose the matrix first
    for (int i = 0; i <= rows; i++) {
        for (int j = i; j <=cols; j++) {
            tmp = B[i][j];
            B[i][j] = B[j][i];
            B[j][i] = tmp;
        }
    }
    // Swap the first and last col and continue until
    // the middle.
    for (int i = 0; i < (cols / 2); i++)
        swap_columns(B, i, cols - i, rows);
}



int _tmain(int argc, _TCHAR* argv[])
{
    int B[ROWS][COLS] = { 
                  {1, 2, 3, 4, 5}, 
                      {6, 7, 8, 9, 10},
                          {11, 12, 13, 14, 15},
                          {16, 17, 18, 19, 20},
                          {21, 22, 23, 24, 25}
                        };

    matrix_2d_rotation(B, ROWS - 1, COLS - 1);

    print_matrix_b(B, ROWS - 1, COLS -1);
    return 0;
}

1

@dagorym: แย่จัง ฉันได้รับการแขวนลงบนนี้เป็นปริศนาที่ดี "ฉันเบื่อฉันสามารถไตร่ตรอง" ฉันมาด้วยรหัสการย้ายที่ของฉัน แต่มาที่นี่เพื่อค้นหาคุณสวยมากเหมือนกับของฉัน ... อ่า นี่คือในทับทิม

require 'pp'
n = 10
a = []
n.times { a << (1..n).to_a }

pp a

0.upto(n/2-1) do |i|
  i.upto(n-i-2) do |j|
    tmp             = a[i][j]
    a[i][j]         = a[n-j-1][i]
    a[n-j-1][i]     = a[n-i-1][n-j-1]
    a[n-i-1][n-j-1] = a[j][n-i-1]
    a[j][n-i-1]     = tmp
  end
end

pp a

1
short normal[4][4] = {{8,4,7,5},{3,4,5,7},{9,5,5,6},{3,3,3,3}};

short rotated[4][4];

for (int r = 0; r < 4; ++r)
{
  for (int c = 0; c < 4; ++c)
  {
    rotated[r][c] = normal[c][3-r];
  }
}

Simple C ++ method จะมีหน่วยความจำขนาดใหญ่อยู่ในอาร์เรย์ขนาดใหญ่


ในบรรดาคำตอบทั้งหมดเหล่านี้ฉันได้พบและทดสอบอันนี้ที่มีขนาดกะทัดรัดและเพียงพอที่จะหมุนได้
dlewin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.