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

Pythonで2Dグリッド(マトリックス)内の島の数を数えるアルゴリズム

2次元のバイナリマトリックス(0と1のみで構成されるグリッド)が与えられたとき、その中に存在する「島」の数を数える問題を考えてみましょう。

ここでいうとは、水に囲まれた陸地の集合であり、隣接する陸地が水平方向または垂直方向につながって形成される領域のことです。斜め方向のつながりは島とはみなしません。また、グリッドの四辺はすべて水に囲まれているものと仮定します。

問題の例

例として、次のようなグリッドを考えてみます。

11000
11000
00100
00011

この場合、色分けしたように陸地(1)のかたまりが3つ存在するため、答えは 3 となります。

解き方のアプローチ

この問題は、DFS(深さ優先探索)を使うことで効率的に解けます。基本的な考え方は以下の通りです。

  • グリッド全体を走査し、まだ訪れていない陸地(値が「1」のセル)を見つけたら、島の数を1つカウントします。
  • そのセルからDFSを開始し、上下左右につながるすべての陸地を「水(0)」に変えていきます。これにより、同じ島を二重にカウントすることを防げます。
  • 走査が終わった時点でのカウントが、島の総数となります。

アルゴリズムの手順

  • メソッド numIslands() で島の数を数え、補助メソッド makeWater() でつながった陸地を水に変えます。
  • グリッドの行数が0の場合は、0を返します。
  • n = 行数、m = 列数、ans = 0 として初期化します。
  • i を 0 から n-1 まで、j を 0 から m-1 までループします。
    • grid[i][j] == "1" の場合、ans を1増やします。
    • makeWater(i, j, n, m, grid) を呼び出して、その島全体を水に変えます。
  • makeWater() はインデックス i, j、行数・列数 n, m、およびグリッドを受け取ります。
    • i < 0 または j < 0 または i >= n または j >= m の場合は、範囲外なので処理を終了します。
    • grid[i][j] が「0」(水)なら何もせず返します。そうでなければ grid[i][j] := "0" として水に変えます。
    • 再帰的に makeWater(i+1, j)makeWater(i, j+1)makeWater(i-1, j)makeWater(i, j-1) を呼び出し、四方の隣接セルも処理します。

Pythonでの実装例

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

class Solution(object):
    def numIslands(self, grid):
        """
        :type grid: List[List[str]]
        :rtype: int
        """
        if len(grid) == 0:
            return 0
        n = len(grid)
        m = len(grid[0])
        ans = 0
        for i in range(n):
            for j in range(m):
                if grid[i][j] == "1":
                    ans += 1
                self.make_water(i, j, n, m, grid)
        return ans

    def make_water(self, i, j, n, m, grid):
        if i < 0 or j < 0 or i >= n or j >= m:
            return
        if grid[i][j] == "0":
            return
        else:
            grid[i][j] = "0"
        self.make_water(i + 1, j, n, m, grid)
        self.make_water(i, j + 1, n, m, grid)
        self.make_water(i - 1, j, n, m, grid)
        self.make_water(i, j - 1, n, m, grid)

入力例

[["1","1","0","0","0"],
 ["1","1","0","0","0"],
 ["0","0","1","0","0"],
 ["0","0","0","1","1"]]

出力結果

3

計算量について

このアルゴリズムでは、各セルは高々一度しか訪問されないため、時間計算量は O(n × m)(nは行数、mは列数)となります。ただし、再帰によるDFSを使用しているため、島が大きい場合には再帰の深さが最大 O(n × m) まで達する可能性があり、Pythonのデフォルトの再帰上限(通常1000)に注意が必要です。大きな入力に対しては、スタックオーバーフローを避けるために反復的なBFS(幅優先探索)や、明示的なスタックを使ったDFSへの書き換えも検討するとよいでしょう。

  1. Pythonでリスト内の最小値を見つける方法を解説

    この記事では、リストの中から最小の数値を見つける方法について、具体的なサンプルコードとともに詳しく解説します。問題の概要問題: 数値のリストが与えられたとき、その中に含まれる最も小さい数値を画面に表示すること。この問題を解くアプローチは主に2つあります。ひとつは sort() メソッドを使ってリストを昇順に並べ替え、先頭の要素(インデックス0)を取得する方法。もうひとつは、Pythonに標準で用意されている組み込み関数 min() を使う方法です。それぞれ順番に見ていきましょう。方法1:sort()メソッドで並べ替えて最小値を取得するまずはリストを昇順にソートし、先頭の要素を取り出す方法です。

  2. Pythonで行列の転置を求めるプログラム

    この記事では、与えられた問題に対する解法とアプローチについて詳しく解説します。 問題文 ある行列が与えられたとき、その転置を同じ行列に格納し、結果を表示する必要があります。 行列の転置とは、行を列に、列を行に入れ替えたものです。言い換えれば、行列Aの転置は、要素A[i][j]をA[j][i]と入れ替えることで得られます。 実装例 N = 4 def transpose(A): for i in range(N): for j in range(i+1, N): A[i][j], A[j][i] = A[j][i], A[i][j] # ドライ