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

Pythonで雨水をトラップするアルゴリズムを解説【スタックを使った実装】

問題の概要

n個の非負整数からなる配列を考えます。この配列は、各バーの幅が1である「標高マップ」を表しており、雨が降ったあとにこの地形へ最大でどれだけの水を溜められるかを計算するのが目的です。いわゆる「Trapping Rain Water(雨水をトラップする)」として知られる有名なアルゴリズム問題です。

イメージは以下のようになります。

Pythonで雨水をトラップするアルゴリズムを解説【スタックを使った実装】

上の図では水たまり(青い部分)が6マスあるため、答えは6になります。

スタックを使った解法の考え方

この問題はスタックを利用すると効率的に解けます。各位置のインデックスをスタックで管理し、現在のバーがスタックの頂点にあるバーより高い場合には、その間に水が溜まっている可能性があるため、スタックから要素を取り出しながら水量を積算していくのが基本のアイデアです。

アルゴリズムの手順

  1. スタック st を用意し、water := 0、i := 0 で初期化します。
  2. i が height の長さ未満である間、以下を繰り返します。
    • スタックが空、または height[スタックの頂点] >= height[i] の場合:i をスタックにプッシュし、i を1増やします。
    • それ以外の場合:
      • x := スタックの頂点の要素を取り出し、スタックから削除します。
      • スタックが空でない場合は、さらに以下を実行します。
        • temp := min(height[スタックの頂点], height[i])
        • dist := i − スタックの頂点 − 1
        • water := water + dist × (temp − height[x])
  3. 最後に water を返します。

Pythonでの実装例

それでは、実際のコードを見て理解を深めましょう。

class Solution(object):
    def trap(self, height):
        stack = []
        water = 0
        i = 0
        while i < len(height):
            if len(stack) == 0 or height[stack[-1]] >= height[i]:
                stack.append(i)
                i += 1
            else:
                x = stack[-1]
                stack.pop()
                if len(stack) != 0:
                    temp = min(height[stack[-1]], height[i])
                    dist = i - stack[-1] - 1
                    water += dist * (temp - height[x])
        return water

ob = Solution()
print(ob.trap([0,1,0,2,1,0,1,3,2,1,2,1]))

入力

[0,1,0,2,1,0,1,3,2,1,2,1]

出力

6

計算量について

各インデックスは最大でも一度しかスタックへのプッシュ・ポップが行われないため、時間計算量は O(n) です。また、スタックに保持される要素数は配列の長さに依存するため、空間計算量も O(n) となります。全探索的なアプローチよりも大幅に効率よく解けるのが、このスタックを使った手法の魅力です。

  1. Pythonで解く「最大の水を溜められるコンテナ」問題 ― 二ポインタ法による効率的な実装

    問題の概要n個の非負整数 a1, a2, ..., an が与えられ、それぞれの値は座標 (i, a[i]) 上の点を表すものとします。i番目の縦線は、端点 (i, a[i]) と (i, 0) を結ぶ線分です。この中から2本の線を選び、x軸とともにコンテナ(容器)を形成したときに、最も多くの水を溜められる組み合わせを見つけるのがこの問題の目的です。例えば、配列が [1,8,6,2,5,4,8,3,7] の場合を考えてみましょう。図の網掛け部分では、高さが7、横幅が7区間あるため、合計面積は 7 × 7 = 49 となります。これが求める出力です。解法のアプローチ(二ポインタ法)この問題は「二

  2. Pythonのリストをスタックとキューとして使う方法を徹底解説

    本記事では、Python 3.x(およびそれ以前のバージョン)におけるスタック(Stack)とキュー(Queue)という基本的なデータ構造について解説します。それぞれのデータ構造の仕組みや操作方法を、実際のコード例とともにわかりやすく学んでいきましょう。 本記事で扱う主なトピックは以下の通りです。 挿入操作(Push / Enqueue) 削除操作(Pop / Dequeue) 表示・走査(トラバース)操作 前提知識:リストとリスト操作の基礎関連するデータ構造:リスト操作 スタック(Stack)とは スタックでは、オブジェクトが積み重なるように格納され、取り出す際には到着した順序とは逆の順