Python
 Computer >> コンピューター >  >> プログラミング >> Python

Pythonで2次元行列の要素を螺旋状(スパイラル順)に出力するプログラム


プログラミングの定番問題のひとつに、「2次元行列(マトリクス)の要素を螺旋状(スパイラル順)に出力する」というものがあります。本記事では、Pythonを使ってこの問題を解くためのアルゴリズムの考え方と実装例を、初心者の方にもわかりやすく解説します。

スパイラル順の出力とは?

2次元行列 mat が与えられたとき、その要素を渦を巻くようにたどりながら出力します。具体的には、まず最初の行(mat[0][0]から)を左から右へすべて出力し、続いて最右列を上から下へ、次に最下行を右から左へ、さらに最左列を下から上へと訪問します。これを内側に向かって繰り返すことで、行列全体を一筆書きのように走査できます。

入力例と出力例

たとえば、次のような 6行 × 3列 の行列が入力として与えられたとします。

7109
291
623
914
275
9911

この場合、期待される出力は次のリストになります。

[7, 10, 9, 1, 3, 4, 5, 11, 9, 9, 2, 9, 6, 2, 9, 2, 1, 7]

出力を見ると、外周(1行目 → 右端列 → 最終行 → 左端列)を一周した後、残りの内側の部分に対して同じ処理が適用されていることが確認できます。

アルゴリズムの考え方

この問題は、「現在の走査範囲を示す4つの境界変数」と「進行方向を示すフラグ」を組み合わせることでエレガントに解けます。手順は以下の通りです。

  • top := 0down := 行数 - 1left := 0right := 列数 - 1 として境界を初期化する
  • res := 空のリスト(結果格納用)、direction := 0(0: 左→右、1: 上→下、2: 右→左、3: 下→上 を表す)
  • top <= down かつ left <= right の間、以下を繰り返す
    • direction が 0 のとき:i を left から right まで動かして matrix[top][i] を res に追加し、その後 top を 1 増やす
    • direction が 1 のとき:i を top から down まで動かして matrix[i][right] を res に追加し、その後 right を 1 減らす
    • direction が 2 のとき:i を right から left まで逆順に動かして matrix[down][i] を res に追加し、その後 down を 1 減らす
    • direction が 3 のとき:i を down から top まで逆順に動かして matrix[i][left] を res に追加し、その後 left を 1 増やす
  • direction := (direction + 1) mod 4 で方向をローテーションする
  • ループを抜けたら res を返す

ポイントは、各行・各列を出力し終えたタイミングで対応する境界変数を必ず1つ内側へ移動させることです。これにより、一度訪問した行や列が二度と走査されなくなり、渦が中心に向かって収束していきます。

Pythonでの実装例

それでは、実際のコードを見てみましょう。

class Solution:
    def solve(self, matrix):
        # 走査範囲の境界を初期化
        top = 0
        down = len(matrix) - 1
        left = 0
        right = len(matrix[0]) - 1
        res = []          # 結果を格納するリスト
        direction = 0     # 0:左→右 / 1:上→下 / 2:右→左 / 3:下→上

        while top <= down and left <= right:
            if direction == 0:          # 上端の行を左から右へ
                for i in range(left, right + 1):
                    res.append(matrix[top][i])
                top += 1
            elif direction == 1:        # 右端の列を上から下へ
                for i in range(top, down + 1):
                    res.append(matrix[i][right])
                right -= 1
            elif direction == 2:        # 下端の行を右から左へ
                for i in range(right, left - 1, -1):
                    res.append(matrix[down][i])
                down -= 1
            else:                       # 左端の列を下から上へ
                for i in range(down, top - 1, -1):
                    res.append(matrix[i][left])
                left += 1

            direction = (direction + 1) % 4
        return res


ob = Solution()
matrix = [
    [7, 10, 9],
    [2, 9, 1],
    [6, 2, 3],
    [9, 1, 4],
    [2, 7, 5],
    [9, 9, 11]
]
print(ob.solve(matrix))

入力

[
    [7, 10, 9],
    [2, 9, 1],
    [6, 2, 3],
    [9, 1, 4],
    [2, 7, 5],
    [9, 9, 11]
]

出力

[7, 10, 9, 1, 3, 4, 5, 11, 9, 9, 2, 9, 6, 2, 9, 2, 1, 7]

計算量について

このアルゴリズムは、行列の全要素をちょうど1回ずつ訪問するため、時間計算量は O(m × n)(m: 行数、n: 列数)です。結果を格納するリストが必要となるため、空間計算量も O(m × n) となります。出力自体が目的の場合、結果リストを介さず直接印刷すれば補助空間を O(1) に抑えることも可能です。

まとめ

スパイラル順の走査は、境界変数(top / down / left / right)と方向フラグを正しく管理することが鍵となります。特に重要なのは、各方向の走査が終わった直後に境界を確実に更新することです。この更新をループの内側で誤って行うと、意図しない要素が取得されたり、無限ループやインデックスエラーが発生したりするため注意しましょう。このパターンはコーディング面接でも頻出のテーマなので、ぜひマスターしておいてください。

  1. C言語で行列の境界要素の合計を求めて出力するプログラム

    行列が与えられたとき、その外周(境界)にある要素だけを出力し、それらの合計を求めて表示するのが本記事の目的です。ここでは、その考え方と具体的なC言語の実装例をわかりやすく解説します。 例 まず、次の3×3の行列を例に考えてみましょう。 入力された行列 1 2 3 4 5 6 7 8 9 境界行列(外周のみ表示) 1 2 3 4   6 7 8 9 このとき、境界要素は「1, 2, 3, 4, 6, 7, 8, 9」であり、中央の「5」は内部要素のため除外されます。 境界要素の合計:1 + 2 + 3 + 4 + 6 + 7 + 8 + 9 = 40 境界要素の合

  2. C言語でO(1)の追加メモリ領域のみを使ってn×nのスパイラル行列を出力する方法

    正の整数 n が与えられたとき、追加の作業用メモリを O(1) しか使用せずに、時計回り方向の n×n スパイラル行列を生成して出力する方法を解説します。スパイラル行列とは、円の原点から出発し、時計回りに渦を描くように値を埋めていく行列のことです。ここでは、2 → 4 → 6 → 8 → 10 → 12 → 14 → 16 → 18 というように偶数を渦状に配置した行列を、O(1) の空間計算量で出力することを目標とします。以下にスパイラル行列の例を示します。実行例入力: 3 出力: 9 8 7 2 1 6 3 4 1メモリを無制限に使えばこの問題は簡単に解けますが、そ