Category: LeetCode Programming
Difficulty: Medium

#114 ๐Ÿ—บ๏ธ 1765. Map of Highest Peak ๐Ÿš€๐Ÿง 

Assigning heights to a matrix may sound straightforward, but this problem adds a fun twist with rules that require thought and logic! ๐Ÿค” Letโ€™s explore this engaging problem with examples, solutions, and walkthroughs! ๐Ÿ’ก


Problem Statement ๐Ÿ“œ

We are given an integer matrix isWater of size m x n that represents a map where:

  • isWater[i][j] == 0: Cell (i, j) is land.
  • isWater[i][j] == 1: Cell (i, j) is water.

The goal is to assign heights to every cell in the matrix such that:

  1. Heights are non-negative.
  2. Water cells have a height of 0.
  3. The absolute difference between the heights of adjacent cells (north, south, east, or west) is at most 1.

Our task is to maximize the maximum height in the matrix while following these rules.

Return a matrix height where height[i][j] represents the height of cell (i, j).


Examples ๐Ÿ–ผ๏ธ

Example 1

Input:

isWater = [[0,1],
           [0,0]]

Output:

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

Explanation:

  • The water cell at (0,1) has a height of 0.
  • Land cells are assigned increasing heights moving away from water.

Example 2

Input:

isWater = [[0,0,1],
           [1,0,0],
           [0,0,0]]

Output:

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

Explanation:

  • Heights increase as we move away from water cells, while maintaining adjacency rules.

Constraints โš™๏ธ

  • \[1 \leq m, n \leq 1000\]
  • \[isWater[i][j] \in \{0, 1\}\]
  • There is at least one water cell.

Approach ๐Ÿ’ก

The heart of solving this problem lies in leveraging Breadth-First Search (BFS) effectively. To understand this better, letโ€™s break down the problem into smaller components and explore the approach in detail.


Key Observations ๐Ÿ”

  1. Water Cells as the Starting Point:
    Since water cells have a height of 0, they serve as the โ€œrootsโ€ of our BFS. From these points, we propagate outward, incrementing the height as we move further away.

  2. Adjacent Rules:
    The problem imposes an adjacency rule that the height difference between two neighboring cells must not exceed 1. BFS ensures that as we process cells level by level, the smallest valid height is assigned to each cell.

  3. Optimization Through BFS:
    BFS guarantees that the first time we encounter a cell, we are processing it with the smallest possible height. This avoids redundant checks and ensures correctness.


How BFS Works in This Context ๐Ÿ› ๏ธ

The BFS here operates as a multi-source BFS:

  • Multi-Source: All water cells (isWater[i][j] == 1) are treated as starting points.
  • Propagation: Heights are propagated level by level, ensuring minimum height differences between adjacent cells.

Step-by-Step Approach ๐Ÿงญ

1. Initialization

  • Create a height matrix of the same size as isWater and initialize all cells to -1, indicating they are unvisited.
  • Identify all water cells (isWater[i][j] == 1) and set their height to 0. Add these cells to a queue as the starting points for BFS.

2. BFS Traversal

  • Use a queue to store the cells to be processed, starting with the water cells.
  • Define the four possible directions of movement (north, south, east, west) as a list of coordinate offsets: [(-1, 0), (1, 0), (0, -1), (0, 1)].
  • For each cell in the queue:
    1. Pop the cell (x, y) from the front of the queue.
    2. For each neighbor (nx, ny) (calculated using the direction offsets):
      • Check if (nx, ny) is within bounds and unvisited (height[nx][ny] == -1).
      • Assign the neighborโ€™s height as height[x][y] + 1.
      • Add (nx, ny) to the queue for further processing.

3. Termination

  • The BFS completes when all cells have been visited and assigned heights. At this point, the height matrix contains the desired solution.

Why BFS? ๐Ÿค”

Using BFS ensures the following:

  1. Minimal Heights: BFS explores all neighbors of a cell before moving further outward, ensuring that each cell receives the smallest valid height.
  2. Efficiency: Each cell is visited only once, making BFS computationally efficient for this problem.
  3. Natural Propagation: Heights naturally increase as BFS explores cells farther from the water sources.

Analysis of the Approach ๐Ÿ“Š

Time Complexity

  • Initialization: \(O(m \cdot n)\) to create the height matrix and enqueue all water cells.
  • BFS Traversal: Each cell is processed once, and all neighbors are checked, leading to a total complexity of \(O(m \cdot n)\).
  • Overall: \(O(m \cdot n)\).

Space Complexity

  • Queue: At most, all cells may be in the queue simultaneously, requiring \(O(m \cdot n)\) space.
  • Height Matrix: The height matrix also requires \(O(m \cdot n)\).
  • Overall: \(O(m \cdot n)\).

Why Not Use DFS? ๐Ÿค”

DFS could theoretically be used for this problem, but itโ€™s less suitable because:

  1. DFS would require backtracking to ensure all valid neighbors are explored with the correct heights.
  2. Managing height propagation correctly with DFS becomes more complex due to its depth-first nature.
  3. BFS inherently aligns with the problemโ€™s requirements of spreading outward in levels, making it a more intuitive and efficient choice.

Edge Case Considerations ๐Ÿงฉ

  1. All Water Cells:
    • If every cell is a water cell, the output will simply be a matrix of zeros. No BFS propagation is needed.
  2. Sparse Water Cells:
    • When water cells are sparse and scattered, BFS ensures efficient propagation from multiple sources simultaneously.
  3. Large Matrices:
    • For \(m, n = 1000\), the solution handles the worst-case scenario efficiently due to its linear time complexity relative to the number of cells.

Real-World Analogy ๐ŸŒ

Imagine youโ€™re standing on an island (water cell) in a flat landscape. You start building steps upward as you move away from the water, ensuring each step is only one unit higher than the previous one. The BFS ensures every island grows its steps outward uniformly until the entire landscape is covered. This process is similar to how heights are propagated in this problem.


Conclusion on the Approach ๐Ÿ

The BFS-based approach for Map of Highest Peak is not just efficient but also intuitive. By treating water cells as starting points and spreading outward, we naturally satisfy all constraints while maximizing the height differences in the matrix. Itโ€™s an elegant solution to a seemingly complex problem! ๐ŸŒŸ


Code Implementation ๐Ÿ’ป

Hereโ€™s the Python implementation:

from collections import deque
from itertools import pairwise

class Solution:
    def highestPeak(self, isWater):
        m, n = len(isWater), len(isWater[0])
        height = [[-1] * n for _ in range(m)]
        queue = deque()

        # Initialize the queue with water cells
        for i in range(m):
            for j in range(n):
                if isWater[i][j] == 1:
                    height[i][j] = 0
                    queue.append((i, j))

        # BFS to calculate heights
        directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
        while queue:
            x, y = queue.popleft()
            for dx, dy in directions:
                nx, ny = x + dx, y + dy
                if 0 <= nx < m and 0 <= ny < n and height[nx][ny] == -1:
                    height[nx][ny] = height[x][y] + 1
                    queue.append((nx, ny))
        
        return height

Time Complexity โฑ๏ธ

  • Initialization: \(O(m \cdot n)\) for creating the matrix and adding water cells to the queue.
  • BFS Traversal: Each cell is visited once, so \(O(m \cdot n)\).
  • Overall: \(O(m \cdot n)\).

Space Complexity ๐Ÿ“ฆ

  • Queue stores up to \(m \cdot n\) elements: \(O(m \cdot n)\).
  • Height matrix: \(O(m \cdot n)\).
  • Total: \(O(m \cdot n)\).

Example Walkthrough ๐Ÿงญ

Letโ€™s walk through Example 2 step-by-step for clarity:

Input:

isWater = [[0,0,1],
           [1,0,0],
           [0,0,0]]
  1. Initialization:
    • Mark all cells as -1 in the height matrix.
    • Add all water cells to the queue:
      queue = [(0,2), (1,0)]
      height = [[-1, -1,  0],
                [ 0, -1, -1],
                [-1, -1, -1]]
      
  2. BFS Iteration 1:
    • Process (0,2):
      • Update (0,1) and (1,2) to 1.
    • Process (1,0):
      • Update (0,0) and (1,1) to 1.
  3. BFS Iteration 2:
    • Process newly updated cells, setting neighbors to 2 and so on.
  4. Final Output:
    height = [[1, 1, 0],
              [0, 1, 1],
              [1, 2, 2]]
    

Edge Cases ๐Ÿงฉ

  1. All Water:
    isWater = [[1,1],
               [1,1]]
    Output: [[0,0],
             [0,0]]
    
  2. All Land except one Water:
    isWater = [[0,0,0],
               [0,1,0],
               [0,0,0]]
    Output: [[2,1,2],
             [1,0,1],
             [2,1,2]]
    
  3. Large Matrix: Ensure it runs within the constraints for \(m, n = 1000\).

Conclusion ๐Ÿ

This problem teaches us to think in terms of level-by-level traversal using BFS. Itโ€™s an excellent example of applying graph algorithms to solve real-world-like matrix problems. ๐ŸŒŸ

We hope this guide helped you understand how to tackle this fun problem! ๐Ÿš€

Written on January 22, 2025