Rotate Image
أُعطيت شبكة n × n من الأعداد الصحيحة، على شكل قائمة لكل صف. أدرها ربع دورة باتجاه عقارب الساعة وأعِدها. يصبح الصف العلوي العمود الأيمن، ويصبح الصف الثاني العمود الذي على يساره، وهكذا: تنتقل القيمة عند (i, j) إلى (j, n-1-i). يمكنك تعديل الشبكة في مكانها؛ أَعِدها.
الدالة
- matrixinteger-2d-array
- الشبكة المربعة، قائمة داخلية واحدة لكل صف
- تُرجعinteger-2d-array
- دارت المصفوفة 90 درجة باتجاه عقارب الساعة
القيود
n == matrix.length == matrix[i].length1 ≤ n ≤ 70-1000 ≤ matrix[i][j] ≤ 1000
أمثلة
- المدخلات
- matrix = [[3, 7, 1], [9, 5, 2], [4, 8, 6]]
- المخرجات
- [[4, 9, 3], [8, 5, 7], [6, 2, 1]]
- الشرح
- يصبح الصف الأول، 3 7 1، العمود الأخير عند قراءته من الأعلى إلى الأسفل. ويصبح العمود الأول، 3 9 4، عند قراءته من الأسفل إلى الأعلى، الصف الأول
[4, 9, 3]. وتبقى القيمة الوسطى 5 في مكانها.
- المدخلات
- matrix = [[1, 0, 0, 2], [0, 5, 6, 0], [0, 7, 8, 0], [3, 0, 0, 4]]
- المخرجات
- [[3, 0, 0, 1], [0, 7, 5, 0], [0, 8, 6, 0], [4, 0, 0, 2]]
- الشرح
- تتحرك كل زاوية بمقدار زاوية واحدة باتجاه عقارب الساعة: من 1 إلى أعلى اليمين، ومن 2 إلى أسفل اليمين، ومن 4 إلى أسفل اليسار، ومن 3 إلى أعلى اليسار. وتدور الكتلة الداخلية 2 × 2 بالطريقة نفسها.
سؤال إضافي
ما الخطوتان الموضعيّتان اللتان تديران المصفوفة 180 درجة، وهل يمكنك فعل ذلك بتمريرة واحدة من عمليات التبديل؟
تلميحات
افتحها واحدًا تلو الآخر. كل تلميح يكشف أكثر قليلًا.
اتبع قيمة واحدة. بعد ربع دورة باتجاه عقارب الساعة، أين ينتهي المطاف بالقيمة الموجودة في الصف 0، العمود 0؟ إلى أين ينتقل باقي الصف 0؟
يصبح الصف
iالعمودn-1-i. تؤدي كتابة القيم مباشرةً في مواضعها الجديدة ضمن المصفوفة نفسها إلى إتلاف القيم التي لم تنقلها بعد، لذا ابحث عن حركتين أبسط لا تبدّلان إلا أزواجًا من القيم.أولًا، انقل المصفوفة: بدّل كل قيمة فوق القطر الرئيسي بصورتها المناظرة أسفله. ثم اعكس ترتيب عناصر كل صف. تحقّق باستخدام مصفوفة 2 × 2 من أن النتيجة دارت باتجاه عقارب الساعة، لا عكس اتجاهها.
الحل
موضع انتقال كل قيمة تحدده صيغة: تنتقل (i, j) إلى (j, n-1-i). تكمن الصعوبة في نقل القيم من دون مصفوفة ثانية، لأن كتابة قيمة في خليتها الجديدة تستبدل قيمة لم تُنقل بعد. إن إجراء تبديل المحاور ثم عكس الصفوف يُنتج ربع دورة الاتجاه نفسها من تبديلات الأزواج.
أنشئ نسخة مُدارة
الفكرة
يصبح الصف i العمود n-1-i، وتنتقل القيمة عند الموضع j في الصف إلى الموضع j نزولًا في ذلك العمود، أي إلى الصف j. لذا result[j][n-1-i] = matrix[i][j]. في المثال الأول، تنتقل القيمة 4 من الصف 2 والعمود 0 إلى الصف 0 والعمود 0، ولهذا تبدأ الإجابة بـ 4.
تتحرك القيم ضمن دورات من أربعة مواضع، لذا فإن الكتابة في المكان نفسه ستمحو قيمة قبل أن تنتقل: ستكتب القيمة 3 فوق القيمة 1 في الزاوية العلوية اليمنى. يتطلب النسخ n² خلايا إضافية، أي 4,900 عندما تكون n = 70.
الخوارزمية
- أنشئ مصفوفة ناتجة
n × n. - لكل صف
iوعمودj، انسخmatrix[i][j]إلىresult[j][n-1-i]. - أعِد النتيجة.
def rotateImage(matrix):
n = len(matrix)
result = [[0] * n for _ in range(n)]
for i in range(n):
for j in range(n):
# Row i becomes column n-1-i, read from top to bottom.
result[j][n - 1 - i] = matrix[i][j]
return resultانقل الصفوف إلى الأعمدة، ثم اعكس ترتيب عناصر كل صف
الفكرة
ينقل التحويل إلى المصفوفة المنقولة (i, j) إلى (j, i)، ويؤدي عكس الصف إلى نقل العمود c إلى العمود n-1-c. معًا، ينقلان (i, j) إلى (j, n-1-i)، وهو الموضع الذي يضعه فيه الدوران تمامًا.
تُنفَّذ العمليتان في المكان لأن كلًّا منهما مجموعة من عمليات تبديل الأزواج. في التحويل إلى المصفوفة المنقولة، يجب أن يكون كلٌّ من matrix[i][j] وmatrix[j][i] في خلية الآخر، لذا يكفي تبديل واحد لإتمام الأمر. بدّل الأزواج التي تحقق j > i فقط، وإلا فسيُعاد تبديل كل زوج إلى موضعه الأصلي. يعكس الصف تبديل طرفيه والتحرك نحو الوسط.
في المثال الأول، ينتج عن التحويل إلى المصفوفة المنقولة [[3, 9, 4], [7, 5, 8], [1, 2, 6]]: يحتوي كل صف الآن على عمود سابق. ويؤدي عكس كل صف إلى النتيجة المطلوبة.
ينفّذ التحويل إلى المصفوفة المنقولة n(n-1)/2 عملية تبديل، وتنفّذ عمليات العكس n·⌊n/2⌋ عملية، لذا يكون العمل O(n²) مع استخدام بضعة متغيرات من الذاكرة الإضافية. ينسخ إصدار R المدخل أولًا إلى مصفوفة R، لذا تبلغ ذاكرته الإضافية O(n²).
الخوارزمية
- لكل خلية فوق القطر الرئيسي،
(i, j)معj > i، بدّل قيمتها مع قيمة الخلية(j, i). - في كل صف، بدّل القيمتين الأولى والأخيرة، وحرّك الطرفين نحو الداخل حتى يلتقيا.
- أعِد المصفوفة.
def rotateImage(matrix):
n = len(matrix)
# Transpose: swap each value above the diagonal with its mirror below it.
for i in range(n):
for j in range(i + 1, n):
matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
# Reverse each row with two pointers.
for row in matrix:
left, right = 0, n - 1
while left < right:
row[left], row[right] = row[right], row[left]
left += 1
right -= 1
return matrix
أخطاء شائعة وحالات حدّية
- إجراء النقل مع بدء
jمن 0. يُبدَّل كل زوج مرتين، ما يلغي النقل، لذا تكون النتيجة عكس الصفوف فقط، كأنها صورة مرآة. - عكس المصفوفة قبل نقلها. هذا الترتيب يدير المصفوفة عكس اتجاه عقارب الساعة. يكشف اختبار بمصفوفة 2 × 2 هذا الخطأ: يجب أن تصبح
[[1, 2], [3, 4]]هكذا:[[3, 1], [4, 2]]. - كتابة
matrix[j][n-1-i] = matrix[i][j]في المصفوفة نفسها. ما تزال الخلية المستهدفة تحتوي على قيمة لم تُنقل، وستُفقد. - استخدام
n-iبدلًا منn-1-i. عند استخدام فهارس تبدأ من 0، يكون العمود الأخير هوn-1، لذا تتجاوزn-iنهاية المصفوفة عندما تكونi = 0.
أسئلة شائعة3
ما التعقيد الزمني وتعقيد المساحة لتدوير مصفوفة؟
يستغرق تدوير مصفوفة n × n زمنًا قدره O(n²)، لأن كل قيمة تقريبًا يجب أن تتحرك مرة واحدة. تحتاج طريقة النقل ثم العكس إلى مساحة إضافية قدرها O(1). كما يستغرق إنشاء نسخة مدوّرة زمنًا قدره O(n²)، لكنه يستخدم مساحة إضافية قدرها O(n²).
كيف تدوّر مصفوفة عكس اتجاه عقارب الساعة؟
بدّل ترتيب الخطوتين: اعكس كل صف أولًا، ثم انقل المصفوفة. يمكنك أيضًا نقل المصفوفة ثم عكس ترتيب الصفوف. في كلتا الحالتين، تنتهي القيمة عند (i, j) في الموضع (n-1-j, i).
هل يمكنك تدوير مصفوفة عن طريق نقل أربع قيم في كل مرة؟
نعم. تنتمي كل قيمة خارج المركز إلى دورة من أربع خلايا: (i, j)، و(j, n-1-i)، و(n-1-i, n-1-j)، و(n-1-j, i). عالج كل حلقة على حدة، واحتفظ بقيمة واحدة من كل دورة في متغير، ثم أزِح القيم الثلاث الأخرى. لها التعقيد الزمني نفسه O(n²) والمساحة O(1)، مع مزيد من حسابات الفهارس التي يمكن أن تخطئ فيها.
مسائل مشابهة
مسائل تعتمد على الأفكار نفسها. حلّ اثنتين أو ثلاث منها يثبّت النمط.
def rotateImage(matrix):
# اكتب الكود هنااختبار #1
اختبار #2
وسيط
matrix = [[3, 7, 1], [9, 5, 2], [4, 8, 6]]
الإخراج المتوقع
[[4, 9, 3], [8, 5, 7], [6, 2, 1]]