Find number of closed islands in given Matrix

Last Updated : 1 Nov, 2025

Given a binary matrix mat[][] where 1 represents land and 0 represents water, find the number of closed islands in the matrix.

An island is defined as a group of 1s connected horizontally or vertically (up, down, left, right).
A closed island is an island that is completely surrounded by water (0) on all four sides and does not touch the boundary of the matrix.

Examples:

Input: mat[][] = [[1, 0, 0], 
[0, 1, 0], 
[0, 0, 1]]
Output: 1
Explanation: The land cell at (1,1) is surrounded on all four sides by 0s (water), so it forms a closed island. The land cells at (0,0) and (2,2) are not closed islands because they lie on the boundary of the matrix.

2


Input: mat[][] = [[0, 0, 0, 0, 0],
[0, 1, 0, 1, 0],
[0, 1, 1, 1, 0],
[0, 0, 0, 0, 1]]
Output: 1
Explanation:  The closed island is the large block of land cells in the center. It is completely surrounded by water on all four sides.
The land cell at (3,4) lies on the boundary of the matrix, so it cannot be considered a closed island.

1
Try It Yourself
redirect icon

[Approach - 1] Using DFS Traversal in Two Pass- O(N × M) and O(N × M) Space

The idea is to use Depth First Search (DFS). First, we traverse all the boundary cells of the matrix. If a boundary cell is land, we perform DFS from that cell to mark all connected land cells as visited — because any land connected to the boundary cannot form a closed island.
After marking all boundary, we traverse the entire grid again. For every unvisited land cell, we perform DFS to explore all connected cells. Each such DFS call represents one closed island.

C++
//Driver Code Starts
#include <iostream>
#include <vector>
using namespace std;
//Driver Code Ends


// DFS to mark all connected land cells as visited
void dfs(vector<vector<int>>& matrix,
         vector<vector<bool>>& visited, int x, int y,
         int n, int m)
{
    
    if (x < 0 || y < 0 || x >= n || y >= m
        || visited[x][y] || matrix[x][y] == 0)
        return;

    visited[x][y] = true;

    // Traverse to all adjacent elements
    dfs(matrix, visited, x + 1, y, n, m);
    dfs(matrix, visited, x - 1, y, n, m);
    dfs(matrix, visited, x, y + 1, n, m);
    dfs(matrix, visited, x, y - 1, n, m);
}

// Count the closed island
int closedIsland(vector<vector<int>>& matrix){
    
    int n = matrix.size(), m = matrix[0].size();
    vector<vector<bool>> visited(n, vector<bool>(m, false));

    // Mark islands connected to the boundary 
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            
            //check it is boundary and not visited
            if ((i * j == 0 || i == n - 1 || j == m - 1)
                && matrix[i][j] == 1 && !visited[i][j]) {
                dfs(matrix, visited, i, j, n, m);
            }
        }
    }

    int result = 0;

    // Count remaining unvisited islands (closed islands)
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            if (matrix[i][j] == 1 && !visited[i][j]) {
                result++;
                dfs(matrix, visited, i, j, n, m);
            }
        }
    }

    return result;
}
 

//Driver Code Starts
int main()
{

    vector<vector<int>> matrix = {
        {0, 0, 0, 0, 0, 0, 0, 1},
        {0, 1, 1, 1, 1, 0, 0, 1},
        {0, 1, 0, 1, 0, 0, 0, 1},
        {0, 1, 1, 1, 1, 0, 1, 0},
        {0, 0, 0, 0, 0, 0, 0, 1}
    };

    cout << closedIsland(matrix);
    return 0;
}

//Driver Code Ends
Java
//Driver Code Starts
class GFG {
//Driver Code Ends


    // DFS to mark all connected land cells as visited
    static void dfs(int[][] matrix, boolean[][] visited,
        int x, int y, int n, int m) {
        
        if (x < 0 || y < 0 || x >= n || y >= m
            || visited[x][y] || matrix[x][y] == 0)
            return;

        visited[x][y] = true;

        // Traverse to all adjacent elements
        dfs(matrix, visited, x + 1, y, n, m);
        dfs(matrix, visited, x - 1, y, n, m);
        dfs(matrix, visited, x, y + 1, n, m);
        dfs(matrix, visited, x, y - 1, n, m);
    }

// Count the closed island
    static int closedIsland(int[][] matrix) {
        int n = matrix.length, m = matrix[0].length;
        boolean[][] visited = new boolean[n][m];
        
        // Mark islands connected to the boundary 
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                
                  //check it is boundary and not visited
                if ((i * j == 0 || i == n - 1 || j == m - 1)
                    && matrix[i][j] == 1 && !visited[i][j]) {
                    dfs(matrix, visited, i, j, n, m);
                }
            }
        }

        int result = 0;

        // Count remaining unvisited islands (closed islands)
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                if (matrix[i][j] == 1 && !visited[i][j]) {
                    result++;
                    dfs(matrix, visited, i, j, n, m);
                }
            }
        }

        return result;
    }
 

//Driver Code Starts
    public static void main(String[] args) {
 
        int[][] mat = {
            {0, 0, 0, 0, 0, 0, 0, 1},
            {0, 1, 1, 1, 1, 0, 0, 1},
            {0, 1, 0, 1, 0, 0, 0, 1},
            {0, 1, 1, 1, 1, 0, 1, 0},
            {0, 0, 0, 0, 0, 0, 0, 1}
        };

        System.out.print(closedIsland(mat));
    }
}

//Driver Code Ends
Python
# DFS to mark all connected land cells as visited
def dfs(matrix, visited, x, y, n, m):
    if (x < 0 or y < 0 or x >= n or y >= m or
        visited[x][y] or matrix[x][y] == 0):
        return

    visited[x][y] = True

    # Traverse to all adjacent elements
    dfs(matrix, visited, x + 1, y, n, m)
    dfs(matrix, visited, x - 1, y, n, m)
    dfs(matrix, visited, x, y + 1, n, m)
    dfs(matrix, visited, x, y - 1, n, m)

# Count the closed island
def closedIsland(matrix):
    n = len(matrix)
    m = len(matrix[0])
    visited = [[False for _ in range(m)] for _ in range(n)]

    # Mark islands connected to the boundary  
    for i in range(n):
        for j in range(m):
             #check it is boundary and not visited
            if ((i * j == 0 or i == n - 1 or j == m - 1) and
                matrix[i][j] == 1 and not visited[i][j]):
                dfs(matrix, visited, i, j, n, m)

    result = 0

    # Count remaining unvisited islands (closed islands)
    for i in range(n):
        for j in range(m):
            if matrix[i][j] == 1 and not visited[i][j]:
                result += 1
                dfs(matrix, visited, i, j, n, m)


    return result
#Driver Code Starts
 
if __name__ == "__main__":
    matrix = [
        [0, 0, 0, 0, 0, 0, 0, 1],
        [0, 1, 1, 1, 1, 0, 0, 1],
        [0, 1, 0, 1, 0, 0, 0, 1],
        [0, 1, 1, 1, 1, 0, 1, 0],
        [0, 0, 0, 0, 0, 0, 0, 1]
    ]

    print(closedIsland(matrix))

#Driver Code Ends
C#
//Driver Code Starts
using System;

class GFG
{
//Driver Code Ends

    // DFS to mark all connected land cells as visited
    void dfs(int[,] matrix, bool[,] visited, int x, int y, int n, int m)
    {
        if (x < 0 || y < 0 || x >= n || y >= m || visited[x, y] || matrix[x, y] == 0)
            return;

        visited[x, y] = true;

        // Traverse in 4 directions
        dfs(matrix, visited, x + 1, y, n, m);
        dfs(matrix, visited, x - 1, y, n, m);
        dfs(matrix, visited, x, y + 1, n, m);
        dfs(matrix, visited, x, y - 1, n, m);
    }

    // Count the closed islands
    public int closedIslands(int[,] matrix)
    {
        int n = matrix.GetLength(0);
        int m = matrix.GetLength(1);
        bool[,] visited = new bool[n, m];

        // Mark islands connected to the boundary  
        for (int i = 0; i < n; ++i)
        {
            for (int j = 0; j < m; ++j)
            {
                // check if boundary and not visited
                if ((i * j == 0 || i == n - 1 || j == m - 1) &&
                    matrix[i, j] == 1 && !visited[i, j])
                {
                    dfs(matrix, visited, i, j, n, m);
                }
            }
        }

        int result = 0;

        // Count remaining unvisited islands (closed islands)
        for (int i = 0; i < n; ++i)
        {
            for (int j = 0; j < m; ++j)
            {
                if (matrix[i, j] == 1 && !visited[i, j])
                {
                    result++;
                    dfs(matrix, visited, i, j, n, m);
                }
            }
        }

        return result;
    }


//Driver Code Starts
    static void Main(string[] args)
    {
        int[,] matrix = {
            { 0, 0, 0, 0, 0, 0, 0, 1 },
            { 0, 1, 1, 1, 1, 0, 0, 1 },
            { 0, 1, 0, 1, 0, 0, 0, 1 },
            { 0, 1, 1, 1, 1, 0, 1, 0 },
            { 0, 0, 0, 0, 0, 0, 0, 1 }
        };

        GFG sol = new GFG();
        int result = sol.closedIslands(matrix);

        Console.WriteLine(result);
    }
}

//Driver Code Ends
JavaScript
//DFS to mark all connected land cells as visited
function dfs(matrix, visited, x, y, n, m) {
    if (x < 0 || y < 0 || x >= n || y >= m ||
        visited[x][y] || matrix[x][y] === 0) {
        return;
    }

    visited[x][y] = true;
    
    // Traverse to all adjacent elements
    dfs(matrix, visited, x + 1, y, n, m);
    dfs(matrix, visited, x - 1, y, n, m);
    dfs(matrix, visited, x, y + 1, n, m);
    dfs(matrix, visited, x, y - 1, n, m);
}
 
//  Count the closed island
function closedIsland(matrix) {
    
    let n = matrix.length;         
    let m = matrix[0].length; 
    let visited = Array.from({ length: n }, () => Array(m).fill(false));

    // Mark islands connected to the boundary
    for (let i = 0; i < n; i++) {
        for (let j = 0; j < m; j++) {
              //check it is boundary and not visited
            if ((i * j === 0 || i === n - 1 || j === m - 1) &&
                matrix[i][j] === 1 && !visited[i][j]) {
                dfs(matrix, visited, i, j, n, m);
            }
        }
    }

    let result = 0;

    // Count remaining unvisited islands (closed islands)
    for (let i = 0; i < n; i++) {
        for (let j = 0; j < m; j++) {
            if (matrix[i][j] === 1 && !visited[i][j]) {
                result++;
                dfs(matrix, visited, i, j, n, m);
            }
        }
    }

    return result;
}


// Driver code 
//Driver Code Starts
let matrix = [
    [0, 0, 0, 0, 0, 0, 0, 1],
    [0, 1, 1, 1, 1, 0, 0, 1],
    [0, 1, 0, 1, 0, 0, 0, 1],
    [0, 1, 1, 1, 1, 0, 1, 0],
    [0, 0, 0, 0, 0, 0, 0, 1]
];

console.log(closedIsland(matrix));

//Driver Code Ends

Output
2

[Approach - 2] Using DFS Traversal in One Pass – O(N × M) Time and O(N × M) Space

Instead of performing two separate DFS traversals, we perform a single DFS directly from each unvisited land cell. We also maintain a boolean variable (hasBoundary) to track whether the current island touches the boundary.
During DFS, if we encounter any land cell (1) that lies on the boundary, we set hasBoundary variable to true, indicating that this island is not closed. After the DFS traversal for that island completes, we check hasBoundary variable if it’s false, it means the island did not touch any boundary, so we count it as a closed island.

C++
//Driver Code Starts
#include <iostream>
#include <vector>
using namespace std;
//Driver Code Ends


// DFS to mark all connected land cells as visited
void dfs(vector<vector<int>>& matrix,
         vector<vector<bool>>& visited,
         int x, int y, int n, int m, bool &hasBoundary) {

    // Base case
    if (x < 0 || y < 0 || x >= n || y >= m || visited[x][y] || matrix[x][y] == 0)
        return;

    // Mark if the current land cell lies on the boundary
    if (x == 0 || y == 0 || x == n - 1 || y == m - 1)
        hasBoundary = true;

    visited[x][y] = true;

    // Explore all 4 directions
    dfs(matrix, visited, x + 1, y, n, m, hasBoundary);
    dfs(matrix, visited, x - 1, y, n, m, hasBoundary);
    dfs(matrix, visited, x, y + 1, n, m, hasBoundary);
    dfs(matrix, visited, x, y - 1, n, m, hasBoundary);
}

// Count the number of closed islands
int closedIsland(vector<vector<int>>& matrix) {
    int n = matrix.size(), m = matrix[0].size();
    vector<vector<bool>> visited(n, vector<bool>(m, false));
    int result = 0;

    // Start DFS  for land cells 
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            
             //Check it is land cell and not visited
            if ( matrix[i][j] == 1 && !visited[i][j]) {
                bool hasBoundary = false; 
                dfs(matrix, visited, i, j, n, m, hasBoundary);
                
                // Count only if not touching boundary
                if (!hasBoundary) result++; 
            }
        }
    }
    return result;
}


//Driver Code Starts
int main() {
    vector<vector<int>> matrix = {
        {0, 0, 0, 0, 0, 0, 0, 1},
        {0, 1, 1, 1, 1, 0, 0, 1},
        {0, 1, 0, 1, 0, 0, 0, 1},
        {0, 1, 1, 1, 1, 0, 1, 0},
        {0, 0, 0, 0, 0, 0, 0, 1}
    };
    cout << closedIsland(matrix);
    return 0;
}

//Driver Code Ends
Java
//Driver Code Starts
import java.util.Arrays;

public class GFG {
//Driver Code Ends


    // DFS to mark all connected land cells as visited
    static void dfs(int[][] matrix,
                    boolean[][] visited,
                    int x, int y, int n, int m, boolean[] hasBoundary) {

        // Base case
        if (x < 0 || y < 0 || x >= n || y >= m || visited[x][y] || matrix[x][y] == 0)
            return;

        // Mark if the current land cell lies on the boundary
        if (x == 0 || y == 0 || x == n - 1 || y == m - 1)
            hasBoundary[0] = true;

        visited[x][y] = true;

        // Explore all 4 directions
        dfs(matrix, visited, x + 1, y, n, m, hasBoundary);
        dfs(matrix, visited, x - 1, y, n, m, hasBoundary);
        dfs(matrix, visited, x, y + 1, n, m, hasBoundary);
        dfs(matrix, visited, x, y - 1, n, m, hasBoundary);
    }

    // Count the number of closed islands
    static int closedIsland(int[][] matrix) {
        int n = matrix.length, m = matrix[0].length;
        boolean[][] visited = new boolean[n][m];
        int result = 0;

        // Start DFS for land cells 
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                
                 //Check it is land cell and not visited
                if (matrix[i][j] == 1 && !visited[i][j]) {
                    boolean[] hasBoundary = {false};
                    dfs(matrix, visited, i, j, n, m, hasBoundary);
                    
                    // Count only if not touching boundary
                    if (!hasBoundary[0]) result++;
                }
            }
        }
        return result;
    }


//Driver Code Starts
    public static void main(String[] args) {
        int[][] matrix = {
            {0, 0, 0, 0, 0, 0, 0, 1},
            {0, 1, 1, 1, 1, 0, 0, 1},
            {0, 1, 0, 1, 0, 0, 0, 1},
            {0, 1, 1, 1, 1, 0, 1, 0},
            {0, 0, 0, 0, 0, 0, 0, 1}
        };
        System.out.println(closedIsland(matrix));
    }
}

//Driver Code Ends
Python
# DFS to mark all connected land cells as visited
def dfs(matrix, visited, x, y, n, m, hasBoundary):

    # Base case
    if x < 0 or y < 0 or x >= n or y >= m or visited[x][y] or matrix[x][y] == 0:
        return

    # Mark if the current land cell lies on the boundary
    if x == 0 or y == 0 or x == n - 1 or y == m - 1:
        hasBoundary[0] = True

    visited[x][y] = True

    # Explore all 4 directions
    dfs(matrix, visited, x + 1, y, n, m, hasBoundary)
    dfs(matrix, visited, x - 1, y, n, m, hasBoundary)
    dfs(matrix, visited, x, y + 1, n, m, hasBoundary)
    dfs(matrix, visited, x, y - 1, n, m, hasBoundary)


# Count the number of closed islands
def closedIsland(matrix):
    n, m = len(matrix), len(matrix[0])
    visited = [[False for _ in range(m)] for _ in range(n)]
    result = 0

    # Start DFS  for land cells 
    for i in range(n):
        for j in range(m):
            
             #Check it is land cell and not visited
            if matrix[i][j] == 1 and not visited[i][j]:
                hasBoundary = [False]
                dfs(matrix, visited, i, j, n, m, hasBoundary)
                
                # Count only if not touching boundary
                if not hasBoundary[0]:
                    result += 1
    return result



#Driver Code Starts
if __name__ == "__main__":
    matrix = [
        [0, 0, 0, 0, 0, 0, 0, 1],
        [0, 1, 1, 1, 1, 0, 0, 1],
        [0, 1, 0, 1, 0, 0, 0, 1],
        [0, 1, 1, 1, 1, 0, 1, 0],
        [0, 0, 0, 0, 0, 0, 0, 1]
    ]
    print(closedIsland(matrix))

#Driver Code Ends
C#
//Driver Code Starts
using System;

class GFG
{
//Driver Code Ends

    // DFS to mark all connected land cells as visited
    static void dfs(int[,] matrix,
                    bool[,] visited,
                    int x, int y, int n, int m, ref bool hasBoundary)
    {
        // Base case
        if (x < 0 || y < 0 || x >= n || y >= m || visited[x, y] || matrix[x, y] == 0)
            return;

        // Mark if the current land cell lies on the boundary
        if (x == 0 || y == 0 || x == n - 1 || y == m - 1)
            hasBoundary = true;

        visited[x, y] = true;

        // Explore all 4 directions
        dfs(matrix, visited, x + 1, y, n, m, ref hasBoundary);
        dfs(matrix, visited, x - 1, y, n, m, ref hasBoundary);
        dfs(matrix, visited, x, y + 1, n, m, ref hasBoundary);
        dfs(matrix, visited, x, y - 1, n, m, ref hasBoundary);
    }

    // Count the number of closed islands
    static int closedIsland(int[,] matrix)
    {
        int n = matrix.GetLength(0), m = matrix.GetLength(1);
        bool[,] visited = new bool[n, m];
        int result = 0;

        // Start DFS  for land cells 
        for (int i = 0; i < n; ++i)
        {
            for (int j = 0; j < m; ++j)
            {

                 //Check it is land cell and not visited
                if (matrix[i, j] == 1 && !visited[i, j])
                {
                    bool hasBoundary = false;
                    dfs(matrix, visited, i, j, n, m, ref hasBoundary);

                    // Count only if not touching boundary
                    if (!hasBoundary) result++;
                }
            }
        }
        return result;
    }


//Driver Code Starts
    static void Main()
    {
        int[,] matrix = {
            {0, 0, 0, 0, 0, 0, 0, 1},
            {0, 1, 1, 1, 1, 0, 0, 1},
            {0, 1, 0, 1, 0, 0, 0, 1},
            {0, 1, 1, 1, 1, 0, 1, 0},
            {0, 0, 0, 0, 0, 0, 0, 1}
        };

        Console.WriteLine(closedIsland(matrix));
    }
}

//Driver Code Ends
JavaScript
// DFS to mark all connected land cells as visited
function dfs(matrix, visited, x, y, n, m, hasBoundary) {

    // Base case
    if (x < 0 || y < 0 || x >= n || y >= m || visited[x][y] || matrix[x][y] === 0)
        return;

    // Mark if the current land cell lies on the boundary
    if (x === 0 || y === 0 || x === n - 1 || y === m - 1)
        hasBoundary.value = true;

    visited[x][y] = true;

    // Explore all 4 directions
    dfs(matrix, visited, x + 1, y, n, m, hasBoundary);
    dfs(matrix, visited, x - 1, y, n, m, hasBoundary);
    dfs(matrix, visited, x, y + 1, n, m, hasBoundary);
    dfs(matrix, visited, x, y - 1, n, m, hasBoundary);
}

// Count the number of closed islands
function closedIsland(matrix) {
    const n = matrix.length, m = matrix[0].length;
    const visited = Array.from({ length: n }, () => Array(m).fill(false));
    let result = 0;

    // Start DFS  for land cells 
    for (let i = 0; i < n; ++i) {
        for (let j = 0; j < m; ++j) {

             //Check it is land cell and not visited
            if (matrix[i][j] === 1 && !visited[i][j]) {
                let hasBoundary = { value: false };
                dfs(matrix, visited, i, j, n, m, hasBoundary);
                
                // Count only if not touching boundary
                if (!hasBoundary.value) result++;
            }
        }
    }
    return result;
}

//Driver Code
//Driver Code Starts
const matrix = [
    [0, 0, 0, 0, 0, 0, 0, 1],
    [0, 1, 1, 1, 1, 0, 0, 1],
    [0, 1, 0, 1, 0, 0, 0, 1],
    [0, 1, 1, 1, 1, 0, 1, 0],
    [0, 0, 0, 0, 0, 0, 0, 1]
];

console.log(closedIsland(matrix));

//Driver Code Ends

Output
2

[Approach – 3] Using BFS Traversal – O(N × M) Time and O(N × M) Space

The idea is similar to the DFS-based approach, but here we use Breadth-First Search (BFS) instead of DFS to count the number of closed islands.

C++
//Driver Code Starts
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
//Driver Code Ends


// BFS to mark all connected land cells as visited
void dfs(vector<vector<int>>& matrix,
         vector<vector<bool>>& visited,
         int x, int y, int n, int m, bool &hasBoundary) {

    queue<pair<int, int>> q;
    q.push({x, y});
    visited[x][y] = true;

    // Directions: up, down, left, right
    int dx[] = {1, -1, 0, 0};
    int dy[] = {0, 0, 1, -1};

    while (!q.empty()) {
        auto [cx, cy] = q.front();
        q.pop();

        // Mark if the current land cell lies on the boundary
        if (cx == 0 || cy == 0 || cx == n - 1 || cy == m - 1)
            hasBoundary = true;

        // Explore all 4 directions
        for (int dir = 0; dir < 4; ++dir) {
            int nx = cx + dx[dir];
            int ny = cy + dy[dir];

            // Check boundaries and unvisited land
            if (nx >= 0 && ny >= 0 && nx < n && ny < m &&
                !visited[nx][ny] && matrix[nx][ny] == 1) {
                visited[nx][ny] = true;
                q.push({nx, ny});
            }
        }
    }
}

// Count the number of closed islands
int closedIsland(vector<vector<int>>& matrix) {
    int n = matrix.size(), m = matrix[0].size();
    vector<vector<bool>> visited(n, vector<bool>(m, false));
    int result = 0;

    // Start DFS  for land cells 
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {

             //Check it is land cell and not visited
            if (matrix[i][j] == 1 && !visited[i][j]) {
                bool hasBoundary = false;
                dfs(matrix, visited, i, j, n, m, hasBoundary);

                // Count only if not touching boundary
                if (!hasBoundary) result++;
            }
        }
    }
    return result;
}


//Driver Code Starts
int main() {
    vector<vector<int>> matrix = {
        {0, 0, 0, 0, 0, 0, 0, 1},
        {0, 1, 1, 1, 1, 0, 0, 1},
        {0, 1, 0, 1, 0, 0, 0, 1},
        {0, 1, 1, 1, 1, 0, 1, 0},
        {0, 0, 0, 0, 0, 0, 0, 1}
    };
    cout << closedIsland(matrix);
    return 0;
}

//Driver Code Ends
Java
//Driver Code Starts
import java.util.Arrays;
import java.util.Queue;
import java.util.LinkedList;

public class GFG {
//Driver Code Ends


    // BFS to mark all connected land cells as visited
    static void dfs(int[][] matrix,
                    boolean[][] visited,
                    int x, int y, int n, int m, boolean[] hasBoundary) {

        Queue<int[]> q = new LinkedList<>();
        q.offer(new int[]{x, y});
        visited[x][y] = true;

        // Directions: up, down, left, right
        int[] dx = {1, -1, 0, 0};
        int[] dy = {0, 0, 1, -1};

        while (!q.isEmpty()) {
            int[] cell = q.poll();
            int cx = cell[0], cy = cell[1];

            // Mark if the current land cell lies on the boundary
            if (cx == 0 || cy == 0 || cx == n - 1 || cy == m - 1)
                hasBoundary[0] = true;

            // Explore all 4 directions
            for (int dir = 0; dir < 4; ++dir) {
                int nx = cx + dx[dir];
                int ny = cy + dy[dir];

                // Check boundaries and unvisited land
                if (nx >= 0 && ny >= 0 && nx < n && ny < m &&
                    !visited[nx][ny] && matrix[nx][ny] == 1) {
                    visited[nx][ny] = true;
                    q.offer(new int[]{nx, ny});
                }
            }
        }
    }

    // Count the number of closed islands
    static int closedIsland(int[][] matrix) {
        int n = matrix.length, m = matrix[0].length;
        boolean[][] visited = new boolean[n][m];
        int result = 0;

        // Start DFS  for land cells 
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {

                 //Check it is land cell and not visited
                if (matrix[i][j] == 1 && !visited[i][j]) {
                    boolean[] hasBoundary = {false};
                    dfs(matrix, visited, i, j, n, m, hasBoundary);

                    // Count only if not touching boundary
                    if (!hasBoundary[0]) result++;
                }
            }
        }
        return result;
    }


//Driver Code Starts
    public static void main(String[] args) {
        int[][] matrix = {
            {0, 0, 0, 0, 0, 0, 0, 1},
            {0, 1, 1, 1, 1, 0, 0, 1},
            {0, 1, 0, 1, 0, 0, 0, 1},
            {0, 1, 1, 1, 1, 0, 1, 0},
            {0, 0, 0, 0, 0, 0, 0, 1}
        };

        System.out.println(closedIsland(matrix));
    }
}

//Driver Code Ends
Python
#Driver Code Starts
from collections import deque
#Driver Code Ends


# BFS to mark all connected land cells as visited
def dfs(matrix, visited, x, y, n, m, hasBoundary):
    q = deque()
    q.append((x, y))
    visited[x][y] = True

    # Directions: up, down, left, right
    dx = [1, -1, 0, 0]
    dy = [0, 0, 1, -1]

    while q:
        cx, cy = q.popleft()

        # Mark if the current land cell lies on the boundary
        if cx == 0 or cy == 0 or cx == n - 1 or cy == m - 1:
            hasBoundary[0] = True

        # Explore all 4 directions
        for dir in range(4):
            nx, ny = cx + dx[dir], cy + dy[dir]

            # Check boundaries and unvisited land
            if 0 <= nx < n and 0 <= ny < m and not visited[nx][ny] and matrix[nx][ny] == 1:
                visited[nx][ny] = True
                q.append((nx, ny))


# Count the number of closed islands
def closedIsland(matrix):
    n, m = len(matrix), len(matrix[0])
    visited = [[False for _ in range(m)] for _ in range(n)]
    result = 0

    # Start DFS  for land cells 
    for i in range(n):
        for j in range(m):

             #Check it is land cell and not visited
            if matrix[i][j] == 1 and not visited[i][j]:
                hasBoundary = [False]
                dfs(matrix, visited, i, j, n, m, hasBoundary)

                # Count only if not touching boundary
                if not hasBoundary[0]:
                    result += 1
    return result



#Driver Code Starts
if __name__ == "__main__":
    matrix = [
        [0, 0, 0, 0, 0, 0, 0, 1],
        [0, 1, 1, 1, 1, 0, 0, 1],
        [0, 1, 0, 1, 0, 0, 0, 1],
        [0, 1, 1, 1, 1, 0, 1, 0],
        [0, 0, 0, 0, 0, 0, 0, 1]
    ]
    print(closedIsland(matrix))

#Driver Code Ends
C#
//Driver Code Starts
using System;
using System.Collections.Generic;

class GFG
{
//Driver Code Ends

    // BFS to mark all connected land cells as visited
    static void dfs(int[,] matrix,
                    bool[,] visited,
                    int x, int y, int n, int m, ref bool hasBoundary)
    {
        Queue<(int, int)> q = new Queue<(int, int)>();
        q.Enqueue((x, y));
        visited[x, y] = true;

        // Directions: up, down, left, right
        int[] dx = { 1, -1, 0, 0 };
        int[] dy = { 0, 0, 1, -1 };

        while (q.Count > 0)
        {
            var (cx, cy) = q.Dequeue();

            // Mark if the current land cell lies on the boundary
            if (cx == 0 || cy == 0 || cx == n - 1 || cy == m - 1)
                hasBoundary = true;

            // Explore all 4 directions
            for (int dir = 0; dir < 4; ++dir)
            {
                int nx = cx + dx[dir];
                int ny = cy + dy[dir];

                // Check boundaries and unvisited land
                if (nx >= 0 && ny >= 0 && nx < n && ny < m &&
                    !visited[nx, ny] && matrix[nx, ny] == 1)
                {
                    visited[nx, ny] = true;
                    q.Enqueue((nx, ny));
                }
            }
        }
    }

    // Count the number of closed islands
    static int closedIsland(int[,] matrix)
    {
        int n = matrix.GetLength(0), m = matrix.GetLength(1);
        bool[,] visited = new bool[n, m];
        int result = 0;

        // Start DFS  for land cells 
        for (int i = 0; i < n; ++i)
        {
            for (int j = 0; j < m; ++j)
            {

                 //Check it is land cell and not visited
                if (matrix[i, j] == 1 && !visited[i, j])
                {
                    bool hasBoundary = false;
                    dfs(matrix, visited, i, j, n, m, ref hasBoundary);

                    // Count only if not touching boundary
                    if (!hasBoundary) result++;
                }
            }
        }
        return result;
    }


//Driver Code Starts
    static void Main()
    {
        int[,] matrix = {
            {0, 0, 0, 0, 0, 0, 0, 1},
            {0, 1, 1, 1, 1, 0, 0, 1},
            {0, 1, 0, 1, 0, 0, 0, 1},
            {0, 1, 1, 1, 1, 0, 1, 0},
            {0, 0, 0, 0, 0, 0, 0, 1}
        };

        Console.WriteLine(closedIsland(matrix));
    }
}

//Driver Code Ends
JavaScript
// BFS to mark all connected land cells as visited
function dfs(matrix, visited, x, y, n, m, hasBoundary) {
    const q = [];
    q.push([x, y]);
    visited[x][y] = true;

    // Directions: up, down, left, right
    const dx = [1, -1, 0, 0];
    const dy = [0, 0, 1, -1];

    while (q.length > 0) {
        const [cx, cy] = q.shift();

        // Mark if the current land cell lies on the boundary
        if (cx === 0 || cy === 0 || cx === n - 1 || cy === m - 1)
            hasBoundary.value = true;

        // Explore all 4 directions
        for (let dir = 0; dir < 4; ++dir) {
            const nx = cx + dx[dir];
            const ny = cy + dy[dir];

            // Check boundaries and unvisited land
            if (
                nx >= 0 && ny >= 0 && nx < n && ny < m &&
                !visited[nx][ny] && matrix[nx][ny] === 1
            ) {
                visited[nx][ny] = true;
                q.push([nx, ny]);
            }
        }
    }
}

// Count the number of closed islands
function closedIsland(matrix) {
    const n = matrix.length, m = matrix[0].length;
    const visited = Array.from({ length: n }, () => Array(m).fill(false));
    let result = 0;

    // Start DFS  for land cells 
    for (let i = 0; i < n; ++i) {
        for (let j = 0; j < m; ++j) {

             //Check it is land cell and not visited
            if (matrix[i][j] === 1 && !visited[i][j]) {
                const hasBoundary = { value: false };
                dfs(matrix, visited, i, j, n, m, hasBoundary);

                // Count only if not touching boundary
                if (!hasBoundary.value) result++;
            }
        }
    }
    return result;
}

//Driver Code
//Driver Code Starts
const matrix = [
    [0, 0, 0, 0, 0, 0, 0, 1],
    [0, 1, 1, 1, 1, 0, 0, 1],
    [0, 1, 0, 1, 0, 0, 0, 1],
    [0, 1, 1, 1, 1, 0, 1, 0],
    [0, 0, 0, 0, 0, 0, 0, 1]
];

console.log(closedIsland(matrix));

//Driver Code Ends

Output
2

[Approach – 4] Using Disjoint-Set ( Union-Find ) – O(N × M) Time and O(N × M) Space

The main idea is to use the Disjoint Set Union (DSU). Each land cell is treated as a node, and whenever two land cells are adjacent, they are joined into the same set using union operations. Land cells that touch the boundary are marked separately, since any group connected to the boundary cannot form a closed island. After processing the entire grid, the number of distinct sets (or parent nodes) that are not connected to the boundary gives the total number of closed islands.

C++
//Driver Code Starts
#include <iostream>
#include <vector>
#include <numeric>
using namespace std;
//Driver Code Ends


class DSU {
private:
    vector<int> parent;

public:
    DSU(int n) {
        parent.resize(n);
        iota(parent.begin(), parent.end(), 0); 
    }

    int find(int x) {
        while (x != parent[x]) {
            x = parent[x];
        }
        return x;
    }

    void unionFind(int a, int b) {
        int rootA = find(a);
        int rootB = find(b);
        if (rootA != rootB)
            parent[rootA] = rootB;
    }

    bool isRoot(int x) {
        return parent[x] == x;
    }
};

// DFS to mark all boundary-connected land cells as water
void floodFill(vector<vector<int>>& mat, int x, int y, int n, int m) {
    if (x < 0 || y < 0 || x >= m || y >= n || mat[x][y] == 0)
        return;
   // Mark as water
    mat[x][y] = 0; 
    floodFill(mat, x + 1, y, n, m);
    floodFill(mat, x - 1, y, n, m);
    floodFill(mat, x, y + 1, n, m);
    floodFill(mat, x, y - 1, n, m);
}

// Remove all land connected to the boundary
void remBoundIs(vector<vector<int>>& mat) {
    int m = mat.size(), n = mat[0].size();

    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            
            // Check boundary cells
            if (i * j == 0 || i == m - 1 || j == n - 1) {
                if (mat[i][j] == 1) {
                    floodFill(mat, i, j, n, m);
                }
            }
        }
    }
}

// Count the number of closed islands
int closedIsland(vector<vector<int>>& mat) {
    if (mat.empty()) return 0;

    int m = mat.size(), n = mat[0].size();
    
    // Remove open islands connected to boundary
    remBoundIs(mat); 

    vector<pair<int, int>> edges;

    // Create edges for adjacent land cells
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (mat[i][j] == 1) {
                int id = i * n + j;

                // Connect to right neighbor
                if (j + 1 < n && mat[i][j + 1] == 1)
                    edges.push_back({id, id + 1});

                // Connect to bottom neighbor
                if (i + 1 < m && mat[i + 1][j] == 1)
                    edges.push_back({id, (i + 1) * n + j});
            }
        }
    }

    // Initialize DSU
    DSU dsu(m * n);

    // Apply union operations
    for (auto& edge : edges) {
        dsu.unionFind(edge.first, edge.second);
    }

    // Count number of closed islands (distinct root components)
    int count = 0;
    for (int i = 0; i < m * n; i++) {
        if (mat[i / n][i % n] == 1 && dsu.isRoot(i))
            count++;
    }

    return count;
}


//Driver Code Starts
int main() {
    vector<vector<int>> mat = {
        { 0,0,0,0,0,0,0,1 },
        { 0,1,1,1,1,0,0,1 },
        { 0,1,0,1,0,0,0,1 },
        { 0,1,1,1,1,0,1,0 },
        { 0,0,0,0,0,0,0,1 }
    };

    cout << closedIsland(mat);
    return 0;
}

//Driver Code Ends
Java
//Driver Code Starts
import java.util.ArrayList;
//Driver Code Ends


class DSU {
    private int[] parent;

    public DSU(int n) {
        parent = new int[n];
        for (int i = 0; i < n; i++)
            parent[i] = i;
    }

    public int find(int x) {
        while (x != parent[x]) {
            x = parent[x];
        }
        return x;
    }

    public void unionFind(int a, int b) {
        int rootA = find(a);
        int rootB = find(b);
        if (rootA != rootB)
            parent[rootA] = rootB;
    }

    public boolean isRoot(int x) {
        return parent[x] == x;
    }
}

public class GFG {

    // DFS to mark all boundary-connected land cells as water
    static void floodFill(int[][] mat, int x, int y, int n, int m) {
        if (x < 0 || y < 0 || x >= m || y >= n || mat[x][y] == 0)
            return;
        // Mark as water
        mat[x][y] = 0;
        floodFill(mat, x + 1, y, n, m);
        floodFill(mat, x - 1, y, n, m);
        floodFill(mat, x, y + 1, n, m);
        floodFill(mat, x, y - 1, n, m);
    }

    // Remove all land connected to the boundary
    static void remBoundIs(int[][] mat) {
        int m = mat.length, n = mat[0].length;

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {

                // Check boundary cells
                if (i * j == 0 || i == m - 1 || j == n - 1) {
                    if (mat[i][j] == 1) {
                        floodFill(mat, i, j, n, m);
                    }
                }
            }
        }
    }

    // Count the number of closed islands
    static int closedIsland(int[][] mat) {
        if (mat.length == 0) return 0;

        int m = mat.length, n = mat[0].length;

        // Remove open islands connected to boundary
        remBoundIs(mat);

        ArrayList<int[]> edges = new ArrayList<>();

        // Create edges for adjacent land cells
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (mat[i][j] == 1) {
                    int id = i * n + j;

                    // Connect to right neighbor
                    if (j + 1 < n && mat[i][j + 1] == 1)
                        edges.add(new int[]{id, id + 1});

                    // Connect to bottom neighbor
                    if (i + 1 < m && mat[i + 1][j] == 1)
                        edges.add(new int[]{id, (i + 1) * n + j});
                }
            }
        }

        // Initialize DSU
        DSU dsu = new DSU(m * n);

        // Apply union operations
        for (int[] edge : edges) {
            dsu.unionFind(edge[0], edge[1]);
        }

        // Count number of closed islands (distinct root components)
        int count = 0;
        for (int i = 0; i < m * n; i++) {
            if (mat[i / n][i % n] == 1 && dsu.isRoot(i))
                count++;
        }

        return count;
    }


//Driver Code Starts
    public static void main(String[] args) {
        int[][] mat = {
            {0,0,0,0,0,0,0,1},
            {0,1,1,1,1,0,0,1},
            {0,1,0,1,0,0,0,1},
            {0,1,1,1,1,0,1,0},
            {0,0,0,0,0,0,0,1}
        };

        System.out.println(closedIsland(mat));
    }
}

//Driver Code Ends
Python
class DSU:
    def __init__(self, n):
        self.parent = list(range(n))

    def find(self, x):
        while x != self.parent[x]:
            x = self.parent[x]
        return x

    def unionFind(self, a, b):
        rootA = self.find(a)
        rootB = self.find(b)
        if rootA != rootB:
            self.parent[rootA] = rootB

    def isRoot(self, x):
        return self.parent[x] == x


# DFS to mark all boundary-connected land cells as water
def floodFill(mat, x, y, n, m):
    if x < 0 or y < 0 or x >= m or y >= n or mat[x][y] == 0:
        return
    # Mark as water
    mat[x][y] = 0
    floodFill(mat, x + 1, y, n, m)
    floodFill(mat, x - 1, y, n, m)
    floodFill(mat, x, y + 1, n, m)
    floodFill(mat, x, y - 1, n, m)


# Remove all land connected to the boundary
def remBoundIs(mat):
    m, n = len(mat), len(mat[0])
    for i in range(m):
        for j in range(n):
            # Check boundary cells
            if i * j == 0 or i == m - 1 or j == n - 1:
                if mat[i][j] == 1:
                    floodFill(mat, i, j, n, m)


# Count the number of closed islands
def closedIsland(mat):
    if not mat:
        return 0

    m, n = len(mat), len(mat[0])

    # Remove open islands connected to boundary
    remBoundIs(mat)

    edges = []

    # Create edges for adjacent land cells
    for i in range(m):
        for j in range(n):
            if mat[i][j] == 1:
                id = i * n + j

                # Connect to right neighbor
                if j + 1 < n and mat[i][j + 1] == 1:
                    edges.append((id, id + 1))

                # Connect to bottom neighbor
                if i + 1 < m and mat[i + 1][j] == 1:
                    edges.append((id, (i + 1) * n + j))

    # Initialize DSU
    dsu = DSU(m * n)

    # Apply union operations
    for a, b in edges:
        dsu.unionFind(a, b)

    # Count number of closed islands (distinct root components)
    count = 0
    for i in range(m * n):
        if mat[i // n][i % n] == 1 and dsu.isRoot(i):
            count += 1

    return count



#Driver Code Starts
# Driver code
mat = [
    [0, 0, 0, 0, 0, 0, 0, 1],
    [0, 1, 1, 1, 1, 0, 0, 1],
    [0, 1, 0, 1, 0, 0, 0, 1],
    [0, 1, 1, 1, 1, 0, 1, 0],
    [0, 0, 0, 0, 0, 0, 0, 1]
]

print(closedIsland(mat))

#Driver Code Ends
C#
//Driver Code Starts
using System;
using System.Collections.Generic;
//Driver Code Ends


class DSU
{
    private int[] parent;

    public DSU(int n)
    {
        parent = new int[n];
        for (int i = 0; i < n; i++)
            parent[i] = i;
    }

    public int find(int x)
    {
        while (x != parent[x])
        {
            x = parent[x];
        }
        return x;
    }

    public void unionFind(int a, int b)
    {
        int rootA = find(a);
        int rootB = find(b);
        if (rootA != rootB)
            parent[rootA] = rootB;
    }

    public bool isRoot(int x)
    {
        return parent[x] == x;
    }
}

class GFG
{
    // DFS to mark all boundary-connected land cells as water
    static void floodFill(int[,] mat, int x, int y, int n, int m)
    {
        if (x < 0 || y < 0 || x >= m || y >= n || mat[x, y] == 0)
            return;

        // Mark as water
        mat[x, y] = 0;

        floodFill(mat, x + 1, y, n, m);
        floodFill(mat, x - 1, y, n, m);
        floodFill(mat, x, y + 1, n, m);
        floodFill(mat, x, y - 1, n, m);
    }

    // Remove all land connected to the boundary
    static void remBoundIs(int[,] mat)
    {
        int m = mat.GetLength(0);
        int n = mat.GetLength(1);

        for (int i = 0; i < m; i++)
        {
            for (int j = 0; j < n; j++)
            {
                // Check boundary cells
                if (i * j == 0 || i == m - 1 || j == n - 1)
                {
                    if (mat[i, j] == 1)
                    {
                        floodFill(mat, i, j, n, m);
                    }
                }
            }
        }
    }

    // Count the number of closed islands
    static int closedIsland(int[,] mat)
    {
        int m = mat.GetLength(0);
        int n = mat.GetLength(1);

        if (m == 0 || n == 0)
            return 0;

        // Remove open islands connected to boundary
        remBoundIs(mat);

        List<(int, int)> edges = new List<(int, int)>();

        // Create edges for adjacent land cells
        for (int i = 0; i < m; i++)
        {
            for (int j = 0; j < n; j++)
            {
                if (mat[i, j] == 1)
                {
                    int id = i * n + j;

                    // Connect to right neighbor
                    if (j + 1 < n && mat[i, j + 1] == 1)
                        edges.Add((id, id + 1));

                    // Connect to bottom neighbor
                    if (i + 1 < m && mat[i + 1, j] == 1)
                        edges.Add((id, (i + 1) * n + j));
                }
            }
        }

        // Initialize DSU
        DSU dsu = new DSU(m * n);

        // Apply union operations
        foreach (var edge in edges)
        {
            dsu.unionFind(edge.Item1, edge.Item2);
        }

        // Count number of closed islands (distinct root components)
        int count = 0;
        for (int i = 0; i < m * n; i++)
        {
            int r = i / n, c = i % n;
            if (mat[r, c] == 1 && dsu.isRoot(i))
                count++;
        }

        return count;
    }


//Driver Code Starts
    static void Main()
    {
        int[,] mat = {
            {0,0,0,0,0,0,0,1},
            {0,1,1,1,1,0,0,1},
            {0,1,0,1,0,0,0,1},
            {0,1,1,1,1,0,1,0},
            {0,0,0,0,0,0,0,1}
        };

        Console.WriteLine(closedIsland(mat));
    }
}

//Driver Code Ends
JavaScript
class DSU {
    constructor(n) {
        this.parent = Array.from({ length: n }, (_, i) => i);
    }

    find(x) {
        while (x !== this.parent[x]) {
            x = this.parent[x];
        }
        return x;
    }

    unionFind(a, b) {
        const rootA = this.find(a);
        const rootB = this.find(b);
        if (rootA !== rootB)
            this.parent[rootA] = rootB;
    }

    isRoot(x) {
        return this.parent[x] === x;
    }
}

// DFS to mark all boundary-connected land cells as water
function floodFill(mat, x, y, n, m) {
    if (x < 0 || y < 0 || x >= m || y >= n || mat[x][y] === 0)
        return;

    // Mark as water
    mat[x][y] = 0;

    floodFill(mat, x + 1, y, n, m);
    floodFill(mat, x - 1, y, n, m);
    floodFill(mat, x, y + 1, n, m);
    floodFill(mat, x, y - 1, n, m);
}

// Remove all land connected to the boundary
function remBoundIs(mat) {
    const m = mat.length, n = mat[0].length;

    for (let i = 0; i < m; i++) {
        for (let j = 0; j < n; j++) {

            // Check boundary cells
            if (i * j === 0 || i === m - 1 || j === n - 1) {
                if (mat[i][j] === 1) {
                    floodFill(mat, i, j, n, m);
                }
            }
        }
    }
}

// Count the number of closed islands
function closedIsland(mat) {
    if (mat.length === 0) return 0;

    const m = mat.length, n = mat[0].length;

    // Remove open islands connected to boundary
    remBoundIs(mat);

    const edges = [];

    // Create edges for adjacent land cells
    for (let i = 0; i < m; i++) {
        for (let j = 0; j < n; j++) {
            if (mat[i][j] === 1) {
                const id = i * n + j;

                // Connect to right neighbor
                if (j + 1 < n && mat[i][j + 1] === 1)
                    edges.push([id, id + 1]);

                // Connect to bottom neighbor
                if (i + 1 < m && mat[i + 1][j] === 1)
                    edges.push([id, (i + 1) * n + j]);
            }
        }
    }

    // Initialize DSU
    const dsu = new DSU(m * n);

    // Apply union operations
    for (const [a, b] of edges) {
        dsu.unionFind(a, b);
    }

    // Count number of closed islands (distinct root components)
    let count = 0;
    for (let i = 0; i < m * n; i++) {
        const r = Math.floor(i / n);
        const c = i % n;
        if (mat[r][c] === 1 && dsu.isRoot(i))
            count++;
    }

    return count;
}


// Example usage
//Driver Code Starts
const mat = [
    [0, 0, 0, 0, 0, 0, 0, 1],
    [0, 1, 1, 1, 1, 0, 0, 1],
    [0, 1, 0, 1, 0, 0, 0, 1],
    [0, 1, 1, 1, 1, 0, 1, 0],
    [0, 0, 0, 0, 0, 0, 0, 1]
];

console.log(closedIsland(mat));

//Driver Code Ends

Output
2
Comment