これはCracking the Coding Interviewからの質問です。ソリューションは、プログラムが外側のエッジを回転させ、次に内側のエッジを回転させると述べています。ただし、両方のforループのロジックを追跡するのに問題があります。
誰かがコードのロジックを説明できますか(たとえば、なぜ「レイヤー<n/2」、「左->上」と「下->左」という4つのステップを実行するのか)。余談ですが、コーディングのインタビュー中にこれを思いついたときの人の思考プロセスはどうでしょうか?
画像の各ピクセルが4バイトであるNxNマトリックスで表される画像がある場合、画像を90度回転するメソッドを記述します。これを適切に行うことができますか?
public static void rotate(int[][] matrix, int n) {
for (int layer = 0; layer < n / 2; ++layer) {
int first = layer;
int last = n - 1 - layer;
for(int i = first; i < last; ++i) {
int offset = i - first;
int top = matrix[first][i]; // save top
// left -> top
matrix[first][i] = matrix[last-offset][first];
// bottom -> left
matrix[last-offset][first] = matrix[last][last - offset];
// right -> bottom
matrix[last][last - offset] = matrix[i][last];
// top -> right
matrix[i][last] = top; // right <- saved top
}
}
}
概要
サンプルマトリックスが次のようになると考えてください。
ABCD
EFGH
IJKL
MNOP
私の説明では、ABCDは行0と見なされ、EFGHは行1と見なされます。行0の最初のピクセルはAです。
また、私が外側のシェルについて話すとき、私は次のことを言及しています:
ABCD
E H
I L
MNOP
まず、値を移動するコードを見てみましょう。
int top = matrix[first][i]; // save top
最初の行は、一番上の位置に値をキャッシュします。これは、[first] [i]で識別されるマトリックスの最上行の位置を指します。例:A
を保存しています。
// left -> top
matrix[first][i] = matrix[last-offset][first];
次の部分は、左の位置から上の位置に値を移動します。例:M
を取得し、A
がある場所に配置します。
// bottom -> left
matrix[last-offset][first] = matrix[last][last - offset];
次の部分は、値を下の位置から左の位置に移動します。例:P
を取得し、M
がある場所に配置します。
// right -> bottom
matrix[last][last - offset] = matrix[i][last];
次の部分は、値を正しい位置から下の位置に移動します。例:D
を取得し、P
がある場所に配置します。
// top -> right
matrix[i][last] = top; // right <- saved top
最後の部分では、値をキャッシュ(最上位の位置)から正しい位置に移動します。例:A
がある最初のステップからD
を置く。
次のループ
外側のループは、行0から行の総数の半分まで実行されます。これは、行0を回転すると最後の行も回転し、行1を回転すると最後から2番目の行も回転する、というように続くためです。
内部ループは、行の最初のピクセル位置(または列)から最後まで実行されます。行0の場合、これはピクセル0から最後のピクセルまでですが、行1の場合、最初と最後のピクセルは行0の一部として回転するため、これはピクセル1から最後から2番目のピクセルまでです。 。
したがって、外側のループの最初の反復により、外側のシェルが回転します。言い換えると:
ABCD
EFGH
IJKL
MNOP
になる:
MIEA
NFGB
OJKC
PLHD
外側のシェルが時計回りに回転したが、内側のコアは移動していないことを確認してください。
次に、外側のループの2番目の反復により、2番目の行が回転し(最初と最後のピクセルを除く)、次のようになります。
MIEA
NJFB
OKGC
PLHD
上記のJasonによって投稿された回答を読んだ後(それはいいです、そして私が持っていたいくつかの質問を解決しました)ので、私はこのロジックを書いています。これを理解するのに数時間を費やして、みんなと共有したいと思いました。
ここでは多くの変数が使用されており、それぞれの意味を理解することが重要です。
変数 'first'を見ると、それは役に立たず、本質的には 'layer'自体であり、ロジック全体で 'first'はまったく変更されていません。だから私は「最初の」変数を削除しました(そしてそれは機能します、先読みしてください)。
これらの各値が内部forループの反復ごとにどのように変化するかを理解するために、これらの変数の値を出力しました。出力を見て、内側のforループ内のあるコーナーから別のコーナーに移動するとどの値が変化するか、どのレイヤーが単一レイヤーをトラバースしても一定の値を維持し、どの値がレイヤーを変更したときにのみ変化するかを理解します。
内部ループを1回繰り返すと、1つのブロックが移動します。単一のレイヤーを移動するために必要な反復回数は、内側に向かって変化します。変数 'last'は私たちのためにその仕事をします、それは内側のループを制限します(内側のレイヤーを制限し、シェルを超えないようにし、ジェイソンが使用した命名法に基づいて構築します)
出力を検討する時間。
6x6マトリックスを使用しました。
Input:
315 301 755 542 955 33
943 613 233 880 945 280
908 609 504 61 849 551
933 251 706 707 913 917
479 785 634 97 851 745
472 348 104 645 17 273
--------------Starting an iteration of OUTER FOR LOOP------------------
--------------Starting an iteration of inner for loop------------------
layer =0
last =5
i =0
buffer = 315
offset = i-layer = 0
Current Status:
472 301 755 542 955 315
943 613 233 880 945 280
908 609 504 61 849 551
933 251 706 707 913 917
479 785 634 97 851 745
273 348 104 645 17 33
--------------Finished an iteration of inner for loop------------------
--------------Starting an iteration of inner for loop------------------
layer =0
last =5
i =1
buffer = 301
offset = i-layer = 1
Current Status:
472 479 755 542 955 315
943 613 233 880 945 301
908 609 504 61 849 551
933 251 706 707 913 917
17 785 634 97 851 745
273 348 104 645 280 33
--------------Finished an iteration of inner for loop------------------
--------------Starting an iteration of inner for loop------------------
layer =0
last =5
i =2
buffer = 755
offset = i-layer = 2
Current Status:
472 479 933 542 955 315
943 613 233 880 945 301
908 609 504 61 849 755
645 251 706 707 913 917
17 785 634 97 851 745
273 348 104 551 280 33
--------------Finished an iteration of inner for loop------------------
--------------Starting an iteration of inner for loop------------------
layer =0
last =5
i =3
buffer = 542
offset = i-layer = 3
Current Status:
472 479 933 908 955 315
943 613 233 880 945 301
104 609 504 61 849 755
645 251 706 707 913 542
17 785 634 97 851 745
273 348 917 551 280 33
--------------Finished an iteration of inner for loop------------------
--------------Starting an iteration of inner for loop------------------
layer =0
last =5
i =4
buffer = 955
offset = i-layer = 4
Current Status:
472 479 933 908 943 315
348 613 233 880 945 301
104 609 504 61 849 755
645 251 706 707 913 542
17 785 634 97 851 955
273 745 917 551 280 33
--------------Finished an iteration of inner for loop------------------
--------------Finished an iteration of OUTER FOR LOOP------------------
--------------Starting an iteration of OUTER FOR LOOP------------------
--------------Starting an iteration of inner for loop------------------
layer =1
last =4
i =1
buffer = 613
offset = i-layer = 0
Current Status:
472 479 933 908 943 315
348 785 233 880 613 301
104 609 504 61 849 755
645 251 706 707 913 542
17 851 634 97 945 955
273 745 917 551 280 33
--------------Finished an iteration of inner for loop------------------
--------------Starting an iteration of inner for loop------------------
layer =1
last =4
i =2
buffer = 233
offset = i-layer = 1
Current Status:
472 479 933 908 943 315
348 785 251 880 613 301
104 609 504 61 233 755
645 97 706 707 913 542
17 851 634 849 945 955
273 745 917 551 280 33
--------------Finished an iteration of inner for loop------------------
--------------Starting an iteration of inner for loop------------------
layer =1
last =4
i =3
buffer = 880
offset = i-layer = 2
Current Status:
472 479 933 908 943 315
348 785 251 609 613 301
104 634 504 61 233 755
645 97 706 707 880 542
17 851 913 849 945 955
273 745 917 551 280 33
--------------Finished an iteration of inner for loop------------------
--------------Finished an iteration of OUTER FOR LOOP------------------
--------------Starting an iteration of OUTER FOR LOOP------------------
--------------Starting an iteration of inner for loop------------------
layer =2
last =3
i =2
buffer = 504
offset = i-layer = 0
Current Status:
472 479 933 908 943 315
348 785 251 609 613 301
104 634 706 504 233 755
645 97 707 61 880 542
17 851 913 849 945 955
273 745 917 551 280 33
--------------Finished an iteration of inner for loop------------------
--------------Finished an iteration of OUTER FOR LOOP------------------
472 479 933 908 943 315
348 785 251 609 613 301
104 634 706 504 233 755
645 97 707 61 880 542
17 851 913 849 945 955
273 745 917 551 280 33
申し訳ありませんが、ここで何が起こっているのかを理解するために、layer、i、offsetの値がどのように変化するかを熟考する以外に方法はありません。
最後にコード
これは、誰かがもっとプレイしたい場合に備えて、最初に不要なものを削除し、すべての印刷ステートメントを追加したコードです。このコードには、ランダムな行列の初期化と出力もあります。
package com.crackingthecodinginterview.assignments.chap1;
public class Problem6RotateMatrix90 {
public static void main(String args[]){
int[][] matrix = new int[6][6];
initializeMatrix(matrix,6);
System.out.println("Input: ");
printMatrix(matrix,6);
rotate(matrix,6);
printMatrix(matrix,6);
}
public static void rotate(int[][] matrix, int n) {
for (int layer = 0; layer < n / 2; ++layer) {
System.out.println("\n--------------Starting an iteration of OUTER FOR LOOP------------------");
int last = n - 1 - layer;
for(int i = layer; i < last; ++i) {
int offset = i - layer;
int buffer = matrix[layer][i]; // save top
System.out.println("\n--------------Starting an iteration of inner for loop------------------");
System.out.println("layer ="+layer);
System.out.println("last ="+last);
System.out.println("i ="+i);
System.out.println("buffer = "+buffer);
System.out.println("offset = i-layer = "+ offset);
// left -> top
matrix[layer][i] = matrix[last-offset][layer];
// bottom -> left
matrix[last-offset][layer] = matrix[last][last - offset];
// right -> bottom
matrix[last][last - offset] = matrix[i][last];
// top -> right
matrix[i][last] = buffer; // right <- saved top
//print
System.out.println("Current Status: ");
printMatrix(matrix,6);
System.out.println("--------------Finished an iteration of inner for loop------------------");
}
System.out.println("--------------Finished an iteration of OUTER FOR LOOP------------------");
}
}
public static void printMatrix(int[][] matrix,int n){
System.out.print("\n");
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
System.out.print(" "+matrix[i][j]);
}
System.out.print("\n");
}
}
public static void initializeMatrix(int[][] matrix,int n){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
matrix[i][j]=(int) (Math.random() * 1000);
}
}
}
}
このソリューションを確認して、適切に実行してください。
public void rotateMatrix(Pixel[][] matrix) {
for (int i = 0; i < matrix.length / 2; i++) {
for (int j = 0; j < matrix.length - 1 - 2 * i; j++) {
Pixel tmp = matrix[j + i][matrix.length - 1 - i];
matrix[j + i][matrix.length - 1 - i] = matrix[i][j + i];
matrix[i][j + i] = matrix[matrix.length - 1 - j - i][i];
matrix[matrix.length - 1 - j - i][i] = matrix[matrix.length - 1 - i][matrix.length - 1 - j - i];
matrix[matrix.length - 1 - i][matrix.length - 1 - j - i] = tmp;
}
}
}
「最後の-オフセット」をリファクタリングすることでコードを記述するより簡単な方法があることを確認しました。
public static void rotateInPlace90DegreesClockwise(int[][] matrix) {
int n = matrix.length;
int half = n / 2;
for (int layer = 0; layer < half; layer++) {
int first = layer;
int last = n - 1 - layer;
for (int i = first; i < last; i++) {
int offset = i - first;
int j = last - offset;
int top = matrix[first][i]; // save top
// left -> top
matrix[first][i] = matrix[j][first];
// bottom -> left
matrix[j][first] = matrix[last][j];
// right -> bottom
matrix[last][j] = matrix[i][last];
// top -> right
matrix[i][last] = top; // right <- saved top
}
}
}
JavaScriptでの私の解決策は次のとおりです。行と列の間の値を右上のエッジから開始し、左下のペアが入れ替えられるまで内側に向かって入れ替えます。
function rotateMatrix(arr) {
var n = arr.length - 1;
for (var i = 0; i < n; i++) {
for (var j = 0; j < n - i; j++) {
var temp = arr[i][j];
arr[i][j] = arr[n - j][n - i]; // top row
arr[n - j][n - i] = temp; // right column
}
}
return arr;
}
これがC#のソリューションです。すべてのN x N
行列には床があります(N/2)
平方サイクル。
たとえば- どちらも 4×4
&5×5
には2つの回転可能なレイヤーがあります。
次のコーナーの組み合わせは、ポジションを識別します。
(top,left) points to (first,first) --> 0,0
(top,right) points to (first,last) --> 0,n
(bottom,left) points to (last,first) --> n,0
(bottom,right) points to (last,last) --> n,n
行列を90度回転させるコードは次のとおりです。
public static void RotateMatrixBy90Degress(int[][] matrix)
{
int matrixLen = matrix.Length;
for (int layer = 0; layer < matrixLen / 2; layer++)
{
int first = layer;
int last = matrixLen - first - 1;
for (int i = first; i < last; i++)
{
int offset = i - first;
int lastMinusOffset = last - offset;
// store variable in a temporary variable
int top = matrix[first][i];
// move values from left --> top
matrix[first][i] = matrix[lastMinusOffset][first];
// move values from bottom --> left
matrix[lastMinusOffset][first] = matrix[last][lastMinusOffset];
// move values from right --> bottom
matrix[last][lastMinusOffset] = matrix[i][last];
// move values from top --> right
matrix[i][last] = top;
}
}
}
行列に乱数を生成するコードは次のとおりです。
public static void RotateMatrixImplementation(int len)
{
int[][] matrix = new int[len][];
var random = new Random();
for (int i = 0; i < matrix.Length; i++)
{
matrix[i] = new int[len]; // Create inner array
for (int j = 0; j < matrix[i].Length; j++)
{
//generate random numbers
matrix[i][j] = random.Next(1, Convert.ToInt32(Math.Pow(len, 3)));
}
}
RotateMatrixBy90Degress(matrix);
}
はい、そのコードはかなり醜くて読みにくいです-主に著者が非常に説明的な変数名を使用しなかったためです。私は同じ原理を使用して同じ問題を解決しました(正方行列を同心の正方形のセットとして扱い、外側の正方形から内側の正方形に向かって一度に1つずつ回転します)。これが私の解決策と私の思考プロセスの説明です。
C#を使用しましたが、構文はJavaとほぼ同じです。コピー/貼り付け後、a.Length
をa.length
に変更するだけで、構文的に正しいJavaになります。
void swap(int[][] a, int g, int h, int i, int j) {
int temp = a[g][h];
a[g][h] = a[i][j];
a[i][j] = temp;
}
int[][] rotateImage(int[][] a) {
if (a.Length > 1) {
int topRow = 0, bottomRow = a.Length - 1, leftCol = topRow, rightCol = bottomRow;
while (topRow < bottomRow && leftCol < rightCol) {
swap(a, topRow, leftCol, topRow, rightCol);
swap(a, topRow, leftCol, bottomRow, leftCol);
swap(a, bottomRow, leftCol, bottomRow, rightCol);
for (int i = topRow + 1, j = bottomRow - 1; i < bottomRow && j > topRow; i++, j--) {
swap(a, topRow, i, i, rightCol);
swap(a, topRow, i, bottomRow, j);
swap(a, topRow, i, j, leftCol);
}
topRow++; leftCol++;
bottomRow--; rightCol--;
}
}
return a;
}
変数leftCol
およびrightCol
と同等に保持されているため、変数topRow
およびbottomRow
を潜在的に取り除くことができます。私がそうしない理由は、コードを理解しやすくするためです。
最初に、1x1
マトリックスが指定された場合、元のマトリックスが返されることに注意してください。これは、ピクセルが1つしかないため、回転が不要であることを意味します。
次に、次の2x2
マトリックスが与えられていると想定します。
1 2
3 4
この行列を3つのスワップで回転させることができます。 Top Left -> Top Right
、Top Left -> Bottom Left
、Top Left -> Bottom Right
。
4 1
2 3
ここで、次の3x3
マトリックスが与えられたとします。
1 2 3
4 5 6
7 8 9
内側の四角は1x1
行列の旧友です。 n > 1 && n % 2 != 0
のすべての正方行列が最終的に中央の1x1
に減少することを認識することが重要です。同様に、n > 1 && n % 2 == 0
が中央の2x2
に減少するもの。どちらの場合も同じように処理できます。
再び、外側の正方形のコーナーから始めます。おなじみの以前の3つのスワップ:Top Left -> Top Right
、Top Left -> Bottom Left
、Top Left -> Bottom Right
を使用します。
7 2 1
4 5 6
9 8 3
行列がほとんど回転していることに注意してください。外面の中央にある厄介な4つの値だけです。ただし、これらの値はそれぞれ、回転した角から1箇所だけ離れていることにも注意してください。コーナーの場合と同じ方法でスワップの開始点を固定するパターンを継続する場合、Top Middle -> Right Middle
、Top Middle -> Bottom Middle
、Top Middle -> Left Middle
のように最後の4つの値を回転できます。 。インデックスに関しては、「Top Middle」は「Top Left」に1を加えたものです。同様に、「右中」は「右上」に1を加えたものです。一部のインデックスでは、極端に大きなインデックス(n - 1
)から開始してデクリメントすることが理にかなっています。小さいミドルインデックスをi
と呼び、大きいミドルインデックスをj
と呼びます。
7 4 1
8 5 2
9 6 3
2x2
マトリックスを回転するには3つのスワップが必要です。3x3
マトリックスを回転するには6つのスワップが必要です。一般に、nxn
マトリックスを回転するにはn!
スワップが必要です。私のwhile
ループは、マトリックス内の各同心四角形の角を回転させ(各四角形は前の四角形よりも小さい)、その後、for
ループがエッジに沿った角の間の値を処理します。回転する内部の四角形がなくなるか、残っている唯一の内部の四角形が1x1
行列になるまで、このように続きます。
これがこの問題に対する私の100%の結果提出です
まず、2D arraylistをレイヤーごとに1D arrayListに分割し、次に、回転した1Dマトリックスを再度マトリックス形式に配置します。ポジション
import Java.io.*;
import Java.math.*;
import Java.security.*;
import Java.text.*;
import Java.util.*;
import Java.util.concurrent.*;
import Java.util.function.*;
import Java.util.regex.*;
import Java.util.stream.*;
import static Java.util.stream.Collectors.joining;
import static Java.util.stream.Collectors.toList;
public class Solution {
static List<Integer[]> storePosition = new ArrayList<>();
public static ArrayList<Integer> rotateOneDArray(List<Integer> arr, int K) {
int[] A = arr.stream().mapToInt(i -> i).toArray();
// write your code in Java SE 8
int r = K % (A.length);
int[] ans = new int[A.length];
int y;
for (int i = 0; i < A.length; i++) {
y = i - r;
if (y < 0) {
y += A.length;
}
ans[y] = A[i];
}
return (ArrayList<Integer>) Arrays.stream(ans).boxed().collect(Collectors.toList());
}
static ArrayList<ArrayList<Integer>> getLinearMatrix(List<List<Integer>> matrix) {
ArrayList<ArrayList<Integer>> linear = new ArrayList<ArrayList<Integer>>();
int M = matrix.get(0).size();
int N = matrix.size();
int m = M, n = N, i, j, counter = 0;
Integer[] pos = new Integer[2];
while (m >= 2 && n >= 2) {
i = counter;
j = counter;
ArrayList<Integer> list = new ArrayList<>((m + n - 2) * 2);
while (j < M - counter) {
list.add(matrix.get(i).get(j));
pos = new Integer[2];
pos[0] = i;
pos[1] = j;
storePosition.add(pos);
++j;
}
--j;
++i;
while (i < N - counter) {
list.add(matrix.get(i).get(j));
pos = new Integer[2];
pos[0] = i;
pos[1] = j;
storePosition.add(pos);
++i;
}
--i;
--j;
while (j >= counter) {
list.add(matrix.get(i).get(j));
pos = new Integer[2];
pos[0] = i;
pos[1] = j;
storePosition.add(pos);
--j;
}
++j;
--i;
while (i > counter) {
list.add(matrix.get(i).get(j));
pos = new Integer[2];
pos[0] = i;
pos[1] = j;
storePosition.add(pos);
--i;
}
linear.add(list);
++counter;
m -= 2;
n -= 2;
}
return linear;
}
// Complete the matrixRotation function below.
static void matrixRotation(List<List<Integer>> matrix, int r) {
int m = matrix.get(0).size();
int n = matrix.size();
ArrayList<ArrayList<Integer>> linearMat = getLinearMatrix(matrix);
ArrayList<ArrayList<Integer>> rotatedLinearMat = new ArrayList<ArrayList<Integer>>();
for (int f = 0; f < linearMat.size(); f++) {
rotatedLinearMat.add(f, rotateOneDArray(linearMat.get(f), r));
}
int p = 0;
Integer[][] result = new Integer[n][m];
for (int i = 0; i < rotatedLinearMat.size(); ++i) {
for (int j = 0; j < rotatedLinearMat.get(i).size(); ++j) {
result[storePosition.get(p)[0]][storePosition.get(p)[1]] = rotatedLinearMat.get(i).get(j);
++p;
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
System.out.print(result[i][j] + " ");
}
System.out.println();
}
}
public static void main(String[] args) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
String[] mnr = bufferedReader.readLine().replaceAll("\\s+$", "").split(" ");
int m = Integer.parseInt(mnr[0]);
int n = Integer.parseInt(mnr[1]);
int r = Integer.parseInt(mnr[2]);
List<List<Integer>> matrix = new ArrayList<>();
IntStream.range(0, m).forEach(i -> {
try {
matrix.add(
Stream.of(bufferedReader.readLine().replaceAll("\\s+$", "").split(" "))
.map(Integer::parseInt)
.collect(toList())
);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
});
matrixRotation(matrix, r);
bufferedReader.close();
}
}