<p>There is an <code>m x n</code> rectangular island that borders both the <strong>Pacific Ocean</strong> and <strong>Atlantic Ocean</strong>. The <strong>Pacific Ocean</strong> touches the island&#39;s left and top edges, and the <strong>Atlantic Ocean</strong> touches the island&#39;s right and bottom edges.</p>

<p>The island is partitioned into a grid of square cells. You are given an <code>m x n</code> integer matrix <code>heights</code> where <code>heights[r][c]</code> represents the <strong>height above sea level</strong> of the cell at coordinate <code>(r, c)</code>.</p>

<p>The island receives a lot of rain, and the rain water can flow to neighboring cells directly north, south, east, and west if the neighboring cell&#39;s height is <strong>less than or equal to</strong> the current cell&#39;s height. Water can flow from any cell adjacent to an ocean into the ocean.</p>

<p>Return <em>a <strong>2D list</strong> of grid coordinates </em><code>result</code><em> where </em><code>result[i] = [r<sub>i</sub>, c<sub>i</sub>]</code><em> denotes that rain water can flow from cell </em><code>(r<sub>i</sub>, c<sub>i</sub>)</code><em> to <strong>both</strong> the Pacific and Atlantic oceans</em>.</p>

<p>&nbsp;</p>
<p><strong class="example">Example 1:</strong></p>
<img alt="" src="https://assets.leetcode.com/uploads/2021/06/08/waterflow-grid.jpg" style="width: 400px; height: 400px;" />
<pre>
<strong>Input:</strong> heights = [[1,2,2,3,5],[3,2,3,4,4],[2,4,5,3,1],[6,7,1,4,5],[5,1,1,2,4]]
<strong>Output:</strong> [[0,4],[1,3],[1,4],[2,2],[3,0],[3,1],[4,0]]
<strong>Explanation:</strong> The following cells can flow to the Pacific and Atlantic oceans, as shown below:
[0,4]: [0,4] -&gt; Pacific Ocean 
&nbsp;      [0,4] -&gt; Atlantic Ocean
[1,3]: [1,3] -&gt; [0,3] -&gt; Pacific Ocean 
&nbsp;      [1,3] -&gt; [1,4] -&gt; Atlantic Ocean
[1,4]: [1,4] -&gt; [1,3] -&gt; [0,3] -&gt; Pacific Ocean 
&nbsp;      [1,4] -&gt; Atlantic Ocean
[2,2]: [2,2] -&gt; [1,2] -&gt; [0,2] -&gt; Pacific Ocean 
&nbsp;      [2,2] -&gt; [2,3] -&gt; [2,4] -&gt; Atlantic Ocean
[3,0]: [3,0] -&gt; Pacific Ocean 
&nbsp;      [3,0] -&gt; [4,0] -&gt; Atlantic Ocean
[3,1]: [3,1] -&gt; [3,0] -&gt; Pacific Ocean 
&nbsp;      [3,1] -&gt; [4,1] -&gt; Atlantic Ocean
[4,0]: [4,0] -&gt; Pacific Ocean 
       [4,0] -&gt; Atlantic Ocean
Note that there are other possible paths for these cells to flow to the Pacific and Atlantic oceans.
</pre>

<p><strong class="example">Example 2:</strong></p>

<pre>
<strong>Input:</strong> heights = [[1]]
<strong>Output:</strong> [[0,0]]
<strong>Explanation:</strong> The water can flow from the only cell to the Pacific and Atlantic oceans.
</pre>

<p>&nbsp;</p>
<p><strong>Constraints:</strong></p>

<ul>
	<li><code>m == heights.length</code></li>
	<li><code>n == heights[r].length</code></li>
	<li><code>1 &lt;= m, n &lt;= 200</code></li>
	<li><code>0 &lt;= heights[r][c] &lt;= 10<sup>5</sup></code></li>
</ul>

class Solution:
    from collections import deque
    def initReachabilityMatrices(self, heights):
        m, n = len(heights), len(heights[0])
        self.heights = heights
        self.m, self.n = m, n
        self.pacific = [[False]*n for _ in range(m)]
        self.pacific[0] = [True]*n

        self.atlantic = [[False]*n for _ in range(m)]
        self.atlantic[m-1] = [True]*n

        for i in range(m):
            self.pacific[i][0] = True
            self.atlantic[i][n-1] = True

    def doBFS(self, Q, visited, ocean: str):
        directions = [(0,1), (0,-1), (1,0), (-1,0)]
        if ocean == 'atlantic':
            updateArr = self.atlantic
        elif ocean == 'pacific':
            updateArr = self.pacific
        else:
            raise Exception(f'ocean should be pacific or atlantic. Provided Value -> {ocean}')

        m, n = len(updateArr), len(updateArr[0])
        while Q:
            i, j = Q.popleft()
            # print (f'popping -> {i}, {j}')
            for di, dj in directions:
                ni, nj = i+di, j+dj
                if ni < 0 or nj < 0 or ni >= self.m or nj >= self.n or self.heights[ni][nj] < self.heights[i][j] or (ni, nj) in visited:
                    continue
                updateArr[ni][nj] = True
                visited.add((ni,nj))
                # print (f'adding -> {ni}, {nj}')
                Q.append((ni,nj))



    def pacificAtlantic(self, heights: List[List[int]]) -> List[List[int]]:
        self.initReachabilityMatrices(heights)

        atlanticVisited, pacificVisited = set(), set()
        atlanticQ, pacificQ = deque(), deque()
        res = []
        
        # Add ocean borders
        for i in range(self.m):
            atlanticQ.append((i, self.n-1))
            atlanticVisited.add((i, self.n-1))

            pacificQ.append((i, 0))
            pacificVisited.add((i, 0))

        for j in range(self.n):
            atlanticQ.append((self.m-1, j))
            atlanticVisited.add((self.m-1, j))

            pacificQ.append((0, j))
            pacificVisited.add((0, j))

        # BFS
        self.doBFS(atlanticQ, atlanticVisited, 'atlantic')
        self.doBFS(pacificQ, pacificVisited, 'pacific')

        # Combine
        for i in range(self.m):
            for j in range(self.n):
                if self.pacific[i][j] and self.atlantic[i][j]:
                    res.append([i,j])
        
        return res


18500