行列90度時計回り回転アルゴリズム解析

1. アルゴリズム概要

このアルゴリズムはレイヤーベースの4要素同時回転を使用します。行列を同心円状のレイヤーに分割し、各レイヤーで4つの対応する要素を同時に回転させます。

2. レイヤー構造の理解

3×3行列のレイヤー構造

元の行列:

1
2
3
4
5
6
7
8
9

レイヤー0(外側):

1
2
3
4
5
6
7
8
9

レイヤー1(中心):

1
2
3
4
5
6
7
8
9

4×4行列のレイヤー構造

レイヤー0(外側):

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

レイヤー1(内側):

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

3. 4要素同時回転の仕組み

回転パターンの説明

各レイヤーで、4つの対応する要素を以下の順序で回転させます:

Top (上辺)
Right (右辺)
Bottom (下辺)
Left (左辺)

3×3行列での具体例(レイヤー0)

ステップ1: 初期状態

1
2
3
4
5
6
7
8
9

ステップ2: 回転後

7
4
1
8
5
2
9
6
3

4. アルゴリズムの詳細解析

レイヤー数の計算

// レイヤー数 = Math.floor(n / 2) // 3×3行列 → Math.floor(3/2) = 1レイヤー // 4×4行列 → Math.floor(4/2) = 2レイヤー // 5×5行列 → Math.floor(5/2) = 2レイヤー for (let layer = 0; layer < Math.floor(n / 2); layer++)

なぜMath.floor(n/2)?

• 奇数サイズ: 中央要素は回転不要

• 偶数サイズ: 全要素が回転対象

• 外側から内側へ順次処理

各レイヤーの境界計算

const first = layer; // レイヤーの開始インデックス const last = n - 1 - layer; // レイヤーの終了インデックス // 例: 4×4行列のレイヤー0の場合 // first = 0, last = 4-1-0 = 3 // 処理範囲: [0,0] から [3,3] の外周

4要素の座標計算式

for (let i = first; i < last; i++) { const offset=i - first; // 現在位置からの相対位置 // 4つの対応する要素の座標 const topPos=[first, i]; // 上辺の要素 const rightPos=[i, last]; // 右辺の要素 const bottomPos=[last, last-offset]; // 下辺の要素 const leftPos=[last-offset, first]; // 左辺の要素 }

座標の対応関係:

[0,0]
TOP
[0,1]
TOP
[0,2]
TOP
[0,3]
RIGHT
[1,0]
LEFT
4
8
[1,3]
RIGHT
[2,0]
LEFT
3
6
[2,3]
RIGHT
[3,0]
BOTTOM
[3,1]
BOTTOM
[3,2]
BOTTOM
[3,3]
BOTTOM

回転処理の実行順序

// 1. トップ要素を一時保存 const top = matrix[first][i]; // 2. 時計回りに4つの要素を移動 matrix[first][i] = matrix[last-offset][first]; // left → top matrix[last-offset][first] = matrix[last][last-offset]; // bottom → left matrix[last][last-offset] = matrix[i][last]; // right → bottom matrix[i][last] = top; // top → right (保存値を使用)

なぜこの順序?

• 最初にtop要素を保存しないと上書きされて失われる

• left→top→right→bottom→leftの循環で4要素を同時移動

• 一時変数は1つだけで済む(メモリ効率的)

完全なコード(コメント付き)

function rotate(matrix: number[][]): void { const n = matrix.length; // 外側から内側へレイヤーごとに処理 for (let layer = 0; layer < Math.floor(n / 2); layer++) { const first=layer; // 現在レイヤーの開始位置 const last=n - 1 - layer; // 現在レイヤーの終了位置 // レイヤーの上辺を左から右へ処理(最後の要素は除く) for (let i=first; i < last; i++) { const offset=i - first; // 現在処理中の4要素の座標 // top: matrix[first][i] 例: [0][1] // right: matrix[i][last] 例: [1][3] // bottom: matrix[last][last-offset] 例: [3][2] // left: matrix[last-offset][first] 例: [2][0] const top=matrix[first][i]; // 上要素を退避 // 時計回りに回転(left→top→right→bottom→left) matrix[first][i]=matrix[last - offset][first]; // left → top matrix[last - offset][first]=matrix[last][last - offset]; // bottom → left matrix[last][last - offset]=matrix[i][last]; // right → bottom matrix[i][last]=top; // top → right } } }

5. 座標計算の詳細

4×4行列での座標例(レイヤー0、i=0の場合)

回転前の4要素:

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

座標計算:

layer=0, i=0, offset=0

top: [0][0] = 5

right: [0][3] = 10

bottom: [3][3] = 16

left: [3][0] = 15

回転後の4要素:

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

移動:

15 → [0,0] (left→top)

16 → [3,0] (bottom→left)

10 → [3,3] (right→bottom)

5 → [0,3] (top→right)

6. 完全なデモンストレーション

7. 計算量分析

時間計算量: O(n²)

• 各要素を正確に1回だけ処理

• ネストしたループだが、内部処理は定数時間

• 最適な時間計算量(全要素を移動する必要があるため)

空間計算量: O(1)

• 一時変数のみ使用(top変数のみ)

• 追加の配列やデータ構造は不要

• in-place操作を実現

8. アルゴリズムの利点