Talentd
FleetCode
Make and follow sheets.Sign in

Coloring A Border solution and explanation

MediumArrayBreadth-First SearchDepth-First SearchMatrix7 min read51.8% acceptanceUpdated 9 Apr 2026
Coloring a border || LeetcodePepcoding · 21:34 · 43K viewsWatch on YouTube
9 more videos on this problem
  1. Coloring a border || LeetcodePepcoding · 21:34 · 43K views
  2. (Old) Leetcode 1034 - Depth First Search | Coloring Border | Connected Components of GraphNideesh Terapalli · 14:13 · 2K views
  3. (Remade) Leetcode 1034 - Depth First Search | Coloring Border | Connected Components of GraphNideesh Terapalli · 8:48 · 2K views
  4. Coloring A Border Medium | Module : Graph | In English | Java | Video_1Pepcoding · 17:30 · 1K views
  5. Coloring A Border Medium | Module : Graph | In English | CPP | Video_1Pepcoding · 17:28 · 713 views
  6. leetCode 1034 - Coloring A Border | JSer - algorithm & JavaScriptJSer · 26:13 · 466 views
  7. leetcode 1034. Coloring A Border - traversal: 2 formats: iterative and recursiveCode-Yao · 19:02 · 209 views
  8. 1034. Coloring A Border 边界染色 0296代码魔术师magiciendecode · 11:05 · 208 views
  9. Coloring A Border, Medium, LeetCode 1034H William Polenz · 22:04 · 85 views
  10. No Sound Leetcode 1034: Coloring a BorderExtremeProgrammer · 4:08 · 43 views

The problem

You are given an m x n integer matrix grid, and three integers row, col, and color. Each value in the grid represents the color of the grid square at that location.

Two squares are called adjacent if they are next to each other in any of the 4 directions.

Two squares belong to the same connected component if they have the same color and they are adjacent.

The border of a connected component is all the squares in the connected component that are either adjacent to (at least) a square not in the component, or on the boundary of the grid (the first or last row or column).

You should color the border of the connected component that contains the square grid[row][col] with color.

Return the final grid.

Example 1:

Input: grid = [[1,1],[1,2]], row = 0, col = 0, color = 3
Output: [[3,3],[3,2]]

Example 2:

Input: grid = [[1,2,2],[2,3,2]], row = 0, col = 1, color = 3
Output: [[1,3,3],[2,3,3]]

Example 3:

Input: grid = [[1,1,1],[1,1,1],[1,1,1]], row = 1, col = 1, color = 2
Output: [[2,2,2],[2,1,2],[2,2,2]]

Constraints:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 50
  • 1 <= grid[i][j], color <= 1000
  • 0 <= row < m
  • 0 <= col < n

Approach overview

Problem Overview: You are given a grid of colors and a starting cell. The task is to find the connected component that shares the same color as the starting cell and repaint only its border cells with a new color. A border cell is one that touches the grid boundary or has at least one neighboring cell with a different color.

Approach 1: Depth-First Search (DFS) Traversal (Time: O(m*n), Space: O(m*n))

This approach treats the grid like a graph where each cell connects to its four neighbors. Starting from (row, col), run a DFS to visit all cells belonging to the same connected component. While exploring neighbors, determine whether the current cell lies on the border by checking if it touches the grid boundary or if any adjacent cell has a different color. Border cells are collected in a list (or marked temporarily) and recolored after traversal. DFS works well here because recursion naturally explores connected regions, making it easy to track visited cells and inspect neighbors. The extra space comes from the recursion stack and the visited structure.

DFS solutions commonly rely on a visited matrix or temporarily marking cells to avoid revisiting. Each cell is processed at most once, giving linear complexity relative to the grid size. If you want to strengthen your understanding of recursive grid traversal, review Depth-First Search and common patterns in matrix problems.

Approach 2: Breadth-First Search (BFS) Traversal (Time: O(m*n), Space: O(m*n))

BFS solves the same connected-component problem using a queue instead of recursion. Begin by pushing the starting cell into a queue and expanding level by level. For each cell, inspect the four directions and enqueue neighbors with the same original color that have not been visited. During processing, check whether the current cell touches the boundary or has a neighbor with a different color. If so, mark it as a border candidate.

After BFS finishes exploring the component, update the stored border cells with the new color. BFS is iterative and avoids recursion depth issues, which can be helpful for very large grids. The algorithm still visits each cell once and performs constant neighbor checks, resulting in O(m*n) time complexity and O(m*n) space due to the queue and visited tracking. This pattern is common in Breadth-First Search problems involving grid components.

Recommended for interviews: Both DFS and BFS are accepted optimal solutions with identical complexity. Interviewers usually expect a DFS implementation because it is shorter and highlights your ability to reason about connected components in a grid. Showing the BFS variant demonstrates understanding of traversal tradeoffs and queue-based graph exploration.

Approach 1: Depth-First Search (DFS) Approach

This approach uses a depth-first search (DFS) to explore the grid and find the connected component starting from the given cell. We'll use DFS to identify all cells belonging to the same color as the starting cell. As we perform DFS, we mark cells that are potential borders if they are on the edge of the grid or adjacent to a different color. Finally, we change the color of these border cells to the specified new color.

def colorBorder(grid, row, col, color):
    m, n = len(grid), len(grid[0])
    original_color = grid[row][col]
    border_cells = []
    visited = [[False] * n for _ in range(m)]

    def dfs(x, y):
        if visited[x][y]:
            return
        visited[x][y] = True
        is_border = False
        for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
            nx, ny = x + dx, y + dy
            if 0 <= nx < m and 0 <= ny < n:
                if grid[nx][ny] == original_color:
                    if not visited[nx][ny]:
                        dfs(nx, ny)
                else:
                    is_border = True
            else:
                is_border = True
        if is_border:
            border_cells.append((x, y))

    dfs(row, col)
    for x, y in border_cells:
        grid[x][y] = color
    return grid
class Solution {
    public int[][] colorBorder(int[][] grid, int row, int col, int color) {
        int m = grid.length, n = grid[0].length;
        int originalColor = grid[row][col];
        boolean[][] visited = new boolean[m][n];
        List<int[]> borders = new ArrayList<>();
        dfs(grid, row, col, visited, borders, originalColor);
        for (int[] cell : borders) {
            grid[cell[0]][cell[1]] = color;
        }
        return grid;
    }
    
    private void dfs(int[][] grid, int x, int y, boolean[][] visited, List<int[]> borders, int originalColor) {
        visited[x][y] = true;
        int m = grid.length, n = grid[0].length;
        boolean isBorder = false;
        int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

        for (int[] dir : directions) {
            int nx = x + dir[0], ny = y + dir[1];
            if (nx >= 0 && nx < m && ny >= 0 && ny < n) {
                if (grid[nx][ny] == originalColor) {
                    if (!visited[nx][ny]) dfs(grid, nx, ny, visited, borders, originalColor);
                } else {
                    isBorder = true;
                }
            } else {
                isBorder = true;
            }
        }
        if (isBorder) borders.add(new int[]{x, y});
    }
}
#include <vector>
#include <queue>

using namespace std;

class Solution {
public:
    vector<vector<int>> colorBorder(vector<vector<int>>& grid, int row, int col, int color) {
        int m = grid.size(), n = grid[0].size();
        int originalColor = grid[row][col];
        vector<vector<bool>> visited(m, vector<bool>(n, false));
        vector<pair<int, int>> borders;
        dfs(grid, row, col, visited, borders, originalColor);
        for (auto& cell : borders) {
            grid[cell.first][cell.second] = color;
        }
        return grid;
    }
    
private:
    void dfs(vector<vector<int>>& grid, int x, int y, vector<vector<bool>>& visited, vector<pair<int, int>>& borders, int originalColor) {
        visited[x][y] = true;
        int m = grid.size(), n = grid[0].size();
        bool isBorder = false;
        vector<pair<int, int>> directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

        for (auto& dir : directions) {
            int nx = x + dir.first, ny = y + dir.second;
            if (nx >= 0 && nx < m && ny >= 0 && ny < n) {
                if (grid[nx][ny] == originalColor) {
                    if (!visited[nx][ny]) dfs(grid, nx, ny, visited, borders, originalColor);
                } else {
                    isBorder = true;
                }
            } else {
                isBorder = true;
            }
        }

        if (isBorder) borders.emplace_back(x, y);
    }
};
function colorBorder(grid, row, col, color) {
    let m = grid.length, n = grid[0].length;
    let originalColor = grid[row][col];
    let visited = Array.from({ length: m }, () => Array(n).fill(false));
    let borders = [];
    
    function dfs(x, y) {
        visited[x][y] = true;
        let isBorder = false;
        let directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];

        for (let [dx, dy] of directions) {
            let nx = x + dx, ny = y + dy;
            if (nx >= 0 && nx < m && ny >= 0 && ny < n) {
                if (grid[nx][ny] == originalColor) {
                    if (!visited[nx][ny]) dfs(nx, ny);
                } else {
                    isBorder = true;
                }
            } else {
                isBorder = true;
            }
        }

        if (isBorder) borders.push([x, y]);
    }

    dfs(row, col);
    for (let [x, y] of borders) {
        grid[x][y] = color;
    }
    return grid;
}
#include <stdbool.h>
#include <stdlib.h>

void dfs(int** grid, int m, int n, int x, int y, bool** visited, int originalColor, int** borders, int* borderSize) {
    visited[x][y] = true;
    bool isBorder = false;
    int directions[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    for (int i = 0; i < 4; i++) {
        int nx = x + directions[i][0], ny = y + directions[i][1];
        if (nx >= 0 && nx < m && ny >= 0 && ny < n) {
            if (grid[nx][ny] == originalColor) {
                if (!visited[nx][ny]) dfs(grid, m, n, nx, ny, visited, originalColor, borders, borderSize);
            } else {
                isBorder = true;
            }
        } else {
            isBorder = true;
        }
    }
    if (isBorder) {
        borders[*borderSize][0] = x;
        borders[*borderSize][1] = y;
        (*borderSize)++;
    }
}

int** colorBorder(int** grid, int gridSize, int* gridColSize, int row, int col, int color, int* returnSize, int** returnColumnSizes) {
    int m = gridSize, n = *gridColSize;
    int originalColor = grid[row][col];
    bool** visited = (bool**)malloc(m * sizeof(bool*));
    for (int i = 0; i < m; i++) {
        visited[i] = (bool*)calloc(n, sizeof(bool));
    }
    int** borders = (int**)malloc(m * n * sizeof(int*));
    for (int i = 0; i < m * n; i++) {
        borders[i] = (int*)malloc(2 * sizeof(int));
    }
    int borderSize = 0;
    dfs(grid, m, n, row, col, visited, originalColor, borders, &borderSize);
    for (int i = 0; i < borderSize; i++) {
        grid[borders[i][0]][borders[i][1]] = color;
    }
    *returnSize = gridSize;
    *returnColumnSizes = (int*)malloc(m * sizeof(int));
    for (int i = 0; i < m; i++) {
        (*returnColumnSizes)[i] = n;
    }
    for (int i = 0; i < m; i++) free(visited[i]);
    free(visited);
    for (int i = 0; i < m * n; i++) free(borders[i]);
    free(borders);
    return grid;
}

We start by initializing a matrix to keep track of visited cells and a list to store border cells. Using a DFS function, we recursively visit each cell in the connected component. We mark a cell as a border if it is adjacent to a cell of a different color or is at the grid's edge. After identifying all border cells, we change their color to the new color.

Complexity

Time Complexity: O(M*N) where M and N are the dimensions of the grid, because we potentially visit all cells.
Space Complexity: O(M*N) for the visited matrix and border cells list for storing the recursive call stack.

Try this approach in the editor

Approach 2: Breadth-First Search (BFS) Approach

This approach employs a breadth-first search (BFS) to explore the grid starting from the specified cell. BFS is helpful because it processes nodes at the current 'depth' level fully before moving on to the nodes at the next depth level, making it easy to track border cells by checking neighbors first.

from collections import deque

def colorBorder(grid, row, col, color):
    original_color = grid[row][col]
    m, n = len(grid), len(grid[0])
    queue = deque([(row, col)])
    visited = set([(row, col)])
    borders = []
    directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]

    while queue:
        x, y = queue.popleft()
        is_border = False
        for dx, dy in directions:
            nx, ny = x + dx, y + dy
            if not (0 <= nx < m and 0 <= ny < n):
                is_border = True
            elif grid[nx][ny] != original_color:
                is_border = True
            elif (nx, ny) not in visited:
                visited.add((nx, ny))
                queue.append((nx, ny))
        if is_border:
            borders.append((x, y))

    for x, y in borders:
        grid[x][y] = color

    return grid
import java.util.*;

class Solution {
    public int[][] colorBorder(int[][] grid, int row, int col, int color) {
        int m = grid.length, n = grid[0].length;
        int originalColor = grid[row][col];
        Queue<int[]> queue = new LinkedList<>();
        queue.offer(new int[]{row, col});
        boolean[][] visited = new boolean[m][n];
        visited[row][col] = true;
        List<int[]> borders = new ArrayList<>();
        int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

        while (!queue.isEmpty()) {
            int[] cell = queue.poll();
            int x = cell[0], y = cell[1];
            boolean isBorder = false;

            for (int[] dir : directions) {
                int nx = x + dir[0], ny = y + dir[1];
                if (nx < 0 || nx >= m || ny < 0 || ny >= n || grid[nx][ny] != originalColor) {
                    isBorder = true;
                } else if (!visited[nx][ny]) {
                    visited[nx][ny] = true;
                    queue.offer(new int[]{nx, ny});
                }
            }

            if (isBorder) borders.add(new int[]{x, y});
        }

        for (int[] border : borders) {
            grid[border[0]][border[1]] = color;
        }

        return grid;
    }
}
#include <vector>
#include <queue>

using namespace std;

class Solution {
public:
    vector<vector<int>> colorBorder(vector<vector<int>>& grid, int row, int col, int color) {
        int m = grid.size(), n = grid[0].size();
        int originalColor = grid[row][col];
        queue<pair<int, int>> q;
        q.push({row, col});
        vector<vector<bool>> visited(m, vector<bool>(n, false));
        visited[row][col] = true;
        vector<pair<int, int>> borders;
        vector<pair<int, int>> directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

        while (!q.empty()) {
            auto [x, y] = q.front(); q.pop();
            bool isBorder = false;

            for (const auto& [dx, dy] : directions) {
                int nx = x + dx, ny = y + dy;

                if (nx < 0 || nx >= m || ny < 0 || ny >= n || grid[nx][ny] != originalColor) {
                    isBorder = true;
                } else if (!visited[nx][ny]) {
                    visited[nx][ny] = true;
                    q.push({nx, ny});
                }
            }

            if (isBorder) borders.push_back({x, y});
        }

        for (const auto& border : borders) {
            grid[border.first][border.second] = color;
        }

        return grid;
    }
};
function colorBorder(grid, row, col, color) {
    let m = grid.length, n = grid[0].length;
    let originalColor = grid[row][col];
    let visited = Array.from({ length: m }, () => Array(n).fill(false));
    let borders = [];
    let queue = [[row, col]];
    visited[row][col] = true;
    let directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];

    while (queue.length) {
        let [x, y] = queue.shift();
        let isBorder = false;
        for (let [dx, dy] of directions) {
            let nx = x + dx, ny = y + dy;
            if (nx < 0 || nx >= m || ny < 0 || ny >= n || grid[nx][ny] !== originalColor) {
                isBorder = true;
            } else if (!visited[nx][ny]) {
                visited[nx][ny] = true;
                queue.push([nx, ny]);
            }
        }
        if (isBorder) {
            borders.push([x, y]);
        }
    }

    for (let [x, y] of borders) {
        grid[x][y] = color;
    }
    return grid;
}
using System;
using System.Collections.Generic;

public class Solution
{
    public int[][] ColorBorder(int[][] grid, int row, int col, int color)
    {
        int m = grid.Length, n = grid[0].Length;
        int originalColor = grid[row][col];
        var directions = new[] { new[] { -1, 0 }, new[] { 1, 0 }, new[] { 0, -1 }, new[] { 0, 1 } };
        var queue = new Queue<(int, int)>();
        queue.Enqueue((row, col));
        bool[,] visited = new bool[m, n];
        visited[row, col] = true;
        var borders = new List<(int, int)>();

        while (queue.Count > 0)
        {
            var (x, y) = queue.Dequeue();
            bool isBorder = false;

            foreach (var dir in directions)
            {
                int nx = x + dir[0], ny = y + dir[1];

                if (nx < 0 || nx >= m || ny < 0 || ny >= n || grid[nx][ny] != originalColor)
                {
                    isBorder = true;
                }
                else if (!visited[nx, ny])
                {
                    visited[nx, ny] = true;
                    queue.Enqueue((nx, ny));
                }
            }

            if (isBorder) borders.Add((x, y));
        }

        foreach (var (x, y) in borders)
        {
            grid[x][y] = color;
        }
        return grid;
    }
}

In this solution, we use a queue to perform BFS, starting from the given cell. As we process each cell, we determine if it is a border by checking adjacent cells. If it's a border, it's added to the 'borders' list. Finally, we change the color of all border cells to the specified color.

Complexity

Time Complexity: O(M*N), processing each grid cell.
Space Complexity: O(M*N) using the queue and storing borders.

Try this approach in the editor

Approach 3: Default Approach

function colorBorder(grid: number[][], row: number, col: number, color: number): number[][] {
    const m = grid.length;
    const n = grid[0].length;
    const vis = new Array(m).fill(0).map(() => new Array(n).fill(false));
    const dirs = [-1, 0, 1, 0, -1];
    const dfs = (i: number, j: number, c: number) => {
        vis[i][j] = true;
        for (let k = 0; k < 4; ++k) {
            const x = i + dirs[k];
            const y = j + dirs[k + 1];
            if (x >= 0 && x < m && y >= 0 && y < n) {
                if (!vis[x][y]) {
                    if (grid[x][y] == c) {
                        dfs(x, y, c);
                    } else {
                        grid[i][j] = color;
                    }
                }
            } else {
                grid[i][j] = color;
            }
        }
    };
    dfs(row, col, grid[row][col]);
    return grid;
}
func colorBorder(grid [][]int, row int, col int, color int) [][]int {
	m, n := len(grid), len(grid[0])
	vis := make([][]bool, m)
	for i := range vis {
		vis[i] = make([]bool, n)
	}
	dirs := [5]int{-1, 0, 1, 0, -1}
	var dfs func(int, int, int)
	dfs = func(i, j, c int) {
		vis[i][j] = true
		for k := 0; k < 4; k++ {
			x, y := i+dirs[k], j+dirs[k+1]
			if x >= 0 && x < m && y >= 0 && y < n {
				if !vis[x][y] {
					if grid[x][y] == c {
						dfs(x, y, c)
					} else {
						grid[i][j] = color
					}
				}
			} else {
				grid[i][j] = color
			}
		}
	}
	dfs(row, col, grid[row][col])
	return grid
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Depth-First Search (DFS) Approach

Time Complexity: O(M*N) where M and N are the dimensions of the grid, because we potentially visit all cells.
Space Complexity: O(M*N) for the visited matrix and border cells list for storing the recursive call stack.

Breadth-First Search (BFS) Approach

Time Complexity: O(M*N), processing each grid cell.
Space Complexity: O(M*N) using the queue and storing borders.

Default Approach–

Complexity analysis

ApproachTimeSpaceWhen to Use
Depth-First Search (DFS)O(m*n)O(m*n)Preferred for interviews and recursive grid traversal problems
Breadth-First Search (BFS)O(m*n)O(m*n)Useful when avoiding recursion depth or when iterative traversal is preferred

Frequently asked questions

Is Coloring A Border easy or hard?

Coloring A Border is rated Medium on LeetCode. The challenge comes from correctly identifying border cells while traversing a connected component and ensuring that internal cells remain unchanged. Strong familiarity with BFS or DFS on matrices makes the problem straightforward.

Coloring A Border Python/Java solution

Python and Java implementations typically follow the same logic: run DFS or BFS from the starting cell, track visited cells, detect borders by checking neighbors, and recolor those border cells. The algorithm maintains O(m*n) time complexity and O(m*n) auxiliary space.

How to solve Coloring A Border in O(n)?

Treat the grid as a graph and perform a DFS or BFS from the starting cell. Track visited cells and check four-directional neighbors to determine if a cell is on the boundary of the component. Store border cells separately and recolor them after traversal. The algorithm processes each cell once, giving O(m*n) time complexity.

What is the best approach for Coloring A Border?

The optimal solution uses either Depth-First Search (DFS) or Breadth-First Search (BFS) to traverse the connected component starting from the given cell. During traversal, each cell checks its four neighbors to determine whether it lies on the component boundary. Border cells are recolored after the traversal completes. Both approaches run in O(m*n) time.

Is Coloring A Border asked at Google/Amazon/Meta?

Grid traversal and connected component problems frequently appear in interviews at companies like Amazon, Google, and Meta. Variations involving BFS or DFS on matrices are common because they test graph fundamentals, boundary detection, and careful state tracking.

What data structure is used in Coloring A Border?

The solution primarily uses graph traversal structures. DFS implementations rely on recursion or an explicit stack, while BFS uses a queue. A visited matrix or marking technique is also required to avoid revisiting cells in the same connected component.

What is the time complexity of Coloring A Border?

The time complexity is O(m*n), where m and n are the grid dimensions. Each cell in the connected component is visited at most once, and every visit checks four neighbors. This keeps the work proportional to the total number of cells in the grid.

Comments

Now write it yourselfClose this page and solve Coloring A Border in the editor, without looking.
Practise Coloring A Border