Maximum Stones Removal

Last Updated : 23 Nov, 2025

Given an 2D array of non-negative integers stones[][] where stones[i] = [xi, yi] represents the location of the ith stone on a 2D plane, return the maximum possible number of stones that you can remove.

A stone can be removed if it shares either the same row or same column as another stone that has not been removed.

Note: Each coordinate point in the grid can have at most one stone.

Examples:

Input: stones[][] = [[0, 0], [0, 1], [1, 0], [1, 2], [2, 1], [2, 2]]

420046960

Output: 5
Explanation: One way to remove 5 stones is as follows:
Remove stone [2, 2] because it shares the same row as [2, 1].
Remove stone [2, 1] because it shares the same column as [0, 1].
Remove stone [1, 2] because it shares the same row as [1, 0].
Remove stone [1, 0] because it shares the same column as [0, 0].
Remove stone [0, 1] because it shares the same row as [0, 0].

Stone [0, 0] cannot be removed since it does not share any row/column with another stone still on the plane.

Input: mat[][] = [[0, 0], [0, 2], [1, 1], [2, 0], [2, 2]]

420046961

Output: 3
Explanation: One way to remove 3 stones is as follows:
Remove stone [2, 2] because it shares the same row as [2, 0].
Remove stone [2, 0] because it shares the same column as [0, 0].
Remove stone [0, 2] because it shares the same row as [0, 0].

Stones [0, 0] and [1, 1] cannot be removed since they do not share any row/column with another stone still on the plane.

Key Idea:-

A stone can be removed if there is at least one other stone in the same row or column.

  • Any two stones in the same row or column are connected by an edge.
  • By repeatedly merging the stones connected by an edge, we can form a connected component.

To maximize the number of stones removed, we repeatedly remove the stone that has the fewest dependencies, i.e., the stone with the minimum degree. Removing such a stone minimizes the impact on the overall structure. Once a stone is removed, all edges connected to it (representing row/column relationships with other stones) are also removed, and the degrees of the affected stones are updated accordingly.
We remove stones as long as each one still shares a row or column with another. When a stone no longer has any such neighbor, it can’t be removed, and the process stops.

Further Observation:

Here we observe that in each such component, all stones except one can be removed, because the final remaining stone has no other stone in its row or column to justify its removal.

Maximum number of stones removed = total number of stones − number of connected components.

In order to find the number of connected components in the graph, we can do it by two approaches:

[Expected Approach - 1] - Using DSU

We use a Disjoint Set Union (DSU) structure to group together stones that lie in the same row or column. For every pair of stones, we check whether they share a row or column, and if they do, we merge them in the DSU. This process forms connected components of stones. After all unions are performed, the number of connected components can be determined by counting the distinct parent representatives in the DSU.

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

//Driver Code Ends

// find parent of the component a stone lies in
int findParent(int i, vector<int>& par) {
    if (par[i] == i) return i;
    return par[i] = findParent(par[i], par);
}

// merging components based on ranks
void unionSet(int u, int v, vector<int>& par, vector<int>& rank) {
    int pu = findParent(u, par);
    int pv = findParent(v, par);

    // if both lie in same component, return
    if (pu == pv) return;

    if (rank[pu] == rank[pv]) {
        par[pu] = pv;
        rank[pv]++;
    } else if (rank[pu] > rank[pv]) {
        par[pv] = pu;
    } else {
        par[pu] = pv;
    }
}

int maxRemove(vector<vector<int>>& stones) {
    int n = stones.size();

    // parent denotes the parent node 
    // of the component a stone lies in
    vector<int> par(n), rank(n, 0);

    // initially each stone is in a different component
    for (int i = 0; i < n; i++) {
        par[i] = i;
    }

    // for each pair of stones, we check if 
    // they are in the same row or column
    // in order to merge them 
    for (int i = 0; i < n; i++) {
        for (int j = i + 1; j < n; j++) {

            // to check for same row or column
            if (stones[i][0] == stones[j][0] || stones[i][1] == stones[j][1]) {
                unionSet(i, j, par, rank);
            }
        }
    }

    // set to include different components
    // each having a different parent
    unordered_set<int> components;
    for (int i = 0; i < n; i++) {
        components.insert(findParent(i, par));
    }

    // atleast 1 stone per component 
    // cannot be removed
    return n - components.size();
}

//Driver Code Starts

int main() {
    vector<vector<int>> stones = {{0,0},{0,2},{1,1},{2,0},{2,2}};
    cout << maxRemove(stones);
}

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

class GFG {
    
//Driver Code Ends

    // find parent of the component a stone lies in
    static int findParent( int i, int[] par) {
        if( par[i] == i ) return i;
        return par[i] = findParent(par[i], par);
    }
    
    // merging components based on ranks
    static void union( int u, int v, int[] par, int[] rank) {
        int pu = findParent(u, par);
        int pv = findParent(v, par);
        
        // if both lie in same component, return
        if( pu == pv ) return;
        
        // merging components based on ranks
        if( rank[pu] == rank[pv]) {
            par[pu]=pv;
            rank[pv]++;
        } else if( rank[pu] > rank[pv]) {
            par[pv] = pu;
        } else {
            par[pu] = pv;
        }
    }
    
    static int maxRemove( int[][] stones) {
        int n = stones.length;
        
        // parent denotes the parent node 
        // of the component a stone lies in
        int[] par = new int[n];
        int[] rank = new int[n];
        
        // initially each stone is in a different component
        for( int i = 0;i < n; i++ ) {
            par[i] = i;
        }
        
        // for each pair of stones, we check if 
        // they are in the same row or column
        // in order to merge them 
        for( int i = 0; i < n ; i++ ) {
            for( int j = i+1; j < n ; j++ ) {
                
                // to check for same row or column
                if( stones[i][0] == stones[j][0] || 
                    stones[i][1] == stones[j][1]) {
                    union(i, j, par, rank);
                }
            }
        }
        
        // set to include different components
        // each having a different parent
        HashSet<Integer> components = new HashSet<>();
        for( int i = 0; i < n; i++ ) {
            components.add(findParent(i, par));
        }
        
        // atleast 1 stone per component 
        // cannot be removed
        return n-components.size();
        
    }

//Driver Code Starts
    
    public static void main(String[] args) {
        int[][] stones
            = {{0, 0}, {0, 2}, {1, 1}, {2, 0}, {2, 2}};
               
        System.out.println(maxRemove(stones));
    }
}
//Driver Code Ends
Python
# find parent of the component a stone lies in
def findParent(i, par):
    if par[i] == i:
        return i
    par[i] = findParent(par[i], par)
    return par[i]

# merging components based on ranks
def union(u, v, par, rank):
    pu = findParent(u, par)
    pv = findParent(v, par)

    # if both lie in same component, return
    if pu == pv:
        return

    # merging components based on ranks
    if rank[pu] == rank[pv]:
        par[pu] = pv
        rank[pv] += 1
    elif rank[pu] > rank[pv]:
        par[pv] = pu
    else:
        par[pu] = pv

def maxRemove(stones):
    n = len(stones)

    # parent denotes the parent node 
    # of the component a stone lies in
    par = list(range(n))
    rank = [0] * n

    # initially each stone is in a different component
    for i in range(n):
        par[i] = i

    # for each pair of stones, we check if 
    # they are in the same row or column
    for i in range(n):
        for j in range(i + 1, n):

            # to check for same row or column
            if stones[i][0] == stones[j][0] or stones[i][1] == stones[j][1]:
                union(i, j, par, rank)

    components = set(findParent(i, par) for i in range(n))

    return n - len(components)


#Driver Code Starts
if __name__ == "__main__":
    stones = [[0,0],[0,2],[1,1],[2,0],[2,2]]
    
    print(maxRemove(stones))
#Driver Code Ends
C#
//Driver Code Starts
using System;
using System.Collections.Generic;

class GFG {
//Driver Code Ends

    
    // find parent of the component a stone lies in
    static int findParent(int i, int[] par) {
        if (par[i] == i) return i;
        return par[i] = findParent(par[i], par);
    }

    // merging components based on ranks
    static void union(int u, int v, int[] par, int[] rank) {
        int pu = findParent(u, par);
        int pv = findParent(v, par);

        // if both lie in same component, return
        if (pu == pv) return;

        // merging components based on ranks
        if (rank[pu] == rank[pv]) {
            par[pu] = pv;
            rank[pv]++;
        } else if (rank[pu] > rank[pv]) {
            par[pv] = pu;
        } else {
            par[pu] = pv;
        }
    }

    static int maxRemove(int[][] stones) {
        int n = stones.Length;

        // parent denotes the parent node 
        // of the component a stone lies in
        int[] par = new int[n];
        int[] rank = new int[n];

        // initially each stone is in a different component
        for (int i = 0; i < n; i++) par[i] = i;

        // for each pair of stones, we check if 
        // they are in the same row or column
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {

                // to check for same row or column
                if (stones[i][0] == stones[j][0] || stones[i][1] == stones[j][1]) {
                    union(i, j, par, rank);
                }
            }
        }

        // set to include different components
        HashSet<int> components = new HashSet<int>();
        for (int i = 0; i < n; i++) {
            components.Add(findParent(i, par));
        }

        // atleast 1 stone per component 
        // cannot be removed
        return n - components.Count;
    }

//Driver Code Starts

    static void Main() {
        int[][] stones = new int[][] {
            new int[] {0,0},
            new int[] {0,2},
            new int[] {1,1},
            new int[] {2,0},
            new int[] {2,2}
        };

        Console.WriteLine(maxRemove(stones));
    }
}
//Driver Code Ends
JavaScript
// find parent of the component a stone lies in
function findParent(i, par) {
    if (par[i] === i) return i;
    return par[i] = findParent(par[i], par);
}

// merging components based on ranks
function unionSet(u, v, par, rank) {
    let pu = findParent(u, par);
    let pv = findParent(v, par);

    // if both lie in same component, return
    if (pu === pv) return;

    // merging components based on ranks
    if (rank[pu] === rank[pv]) {
        par[pu] = pv;
        rank[pv]++;
    } else if (rank[pu] > rank[pv]) {
        par[pv] = pu;
    } else {
        par[pu] = pv;
    }
}

function maxRemove(stones) {
    let n = stones.length;

    // parent denotes the parent node 
    // of the component a stone lies in
    let par = Array(n).fill(0).map((_, i) => i);
    let rank = Array(n).fill(0);

    // initially each stone is in a different component
    for (let i = 0; i < n; i++) par[i] = i;

    // for each pair of stones, we check if 
    // they are in the same row or column
    // in order to merge them 
    for (let i = 0; i < n; i++) {
        for (let j = i + 1; j < n; j++) {

            // to check for same row or column
            if (stones[i][0] === stones[j][0] || stones[i][1] === stones[j][1]) {
                unionSet(i, j, par, rank);
            }
        }
    }

    // set to include different components
    let components = new Set();
    for (let i = 0; i < n; i++) {
        components.add(findParent(i, par));
    }

    // atleast 1 stone per component 
    // cannot be removed
    return n - components.size;
}



//Driver Code Starts
// Driver code
let stones = [[0,0],[0,2],[1,1],[2,0],[2,2]];
console.log(maxRemove(stones));

//Driver Code Ends

Output
3

Time complexity: O(n2 + n log n), n2 is because we check for each pair of stone if they are in the same row or column, n log n because for all stones, we call the findParent function that takes O(log n) time.
Auxiliary Space: O(n) for parent and rank array.

[Expected Approach - 2] - Using DFS

In this approach, for every unvisited stone, we start a DFS to explore all stones reachable through these row/column connections. This DFS recursively visits every stone that lies in the same row or column, effectively capturing one entire connected group. Once a DFS finishes, we know one full component is visited and therefore the number of DFS calls made are equal to the number of components in the graph. Since each component must keep one stone, the maximum removable stones are total number of stones − number of components.

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

//Driver Code Ends

void dfs(int i, vector<bool>& v, vector<vector<int>>& stones) {
    if (v[i]) return;
    v[i] = true;

    for (int j = 0; j < stones.size(); j++) {

        // if another stone has same row or 
        // column as this stone then both lie 
        // in the same component
        if (stones[i][0] == stones[j][0] || stones[i][1] == stones[j][1]) {
            dfs(j, v, stones);
        }
    }
}

int maxRemove(vector<vector<int>>& stones) {
    int n = stones.size();
    
    vector<bool> visited(n, false);
    int components = 0;

    for (int i = 0; i < n; i++) {
        
        // visiting the stone if not visited
        // and finding all the stones lying in 
        // the same component as this stone
        if (!visited[i]) {
            dfs(i, visited, stones);
            components++;
        }
    }

    // atleast 1 stone per component 
    // cannot be removed
    return n - components;
}

//Driver Code Starts

int main() {
    vector<vector<int>> stones = {{0,0},{0,2},{1,1},{2,0},{2,2}};
    cout << maxRemove(stones);
}

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

    static int maxRemove( int[][] stones) {
        int n = stones.length;
        
        boolean[] visited = new boolean[n];
        int components = 0;
        for( int i = 0; i < n ; i++ ) {
            
            // visiting the stone if not visited
            // and finding all the stones lying in 
            // the same component as this stone
            if( !visited[i] ) {
                dfs(i, visited, stones);
                components++;
            }
        }
        // atleast 1 stone per component 
        // cannot be removed
        return n-components;
        
    }
    static void dfs( int i, boolean[] v, int[][] stones ) {
        if( v[i] ) return;
        v[i] = true;
        
        for( int j = 0; j < stones.length; j++ ) {
            
            // if another stone has same row or 
            // column as this stone then both lie 
            // in the same component
            if( stones[i][0] == stones[j][0] || stones[i][1] == stones[j][1] ) {
                dfs(j, v, stones);
            }
        }
        
    }

//Driver Code Starts
    
    public static void main(String[] args) {
        int[][] stones
            = {{0, 0}, {0, 2}, {1, 1}, {2, 0}, {2, 2}};
               
        System.out.println(maxRemove(stones));
    }
}
//Driver Code Ends
Python
def dfs(i, visited, stones):
    if visited[i]:
        return
    visited[i] = True

    for j in range(len(stones)):

        # if another stone has same row or 
        # column as this stone then both lie 
        # in the same component
        if stones[i][0] == stones[j][0] or stones[i][1] == stones[j][1]:
            dfs(j, visited, stones)

def maxRemove(stones):
    n = len(stones)

    visited = [False] * n
    components = 0

    for i in range(n):

        # visiting the stone if not visited
        # and finding all the stones lying in 
        # the same component as this stone
        if not visited[i]:
            dfs(i, visited, stones)
            components += 1

    # atleast 1 stone per component 
    # cannot be removed
    return n - components


#Driver Code Starts
if __name__ == "__main__":
    stones = [[0,0],[0,2],[1,1],[2,0],[2,2]]
    print(maxRemove(stones))
#Driver Code Ends
C#
//Driver Code Starts
using System;
using System.Collections.Generic;

class GFG {

//Driver Code Ends

    static void dfs(int i, bool[] v, int[][] stones) {
        if (v[i]) return;
        v[i] = true;

        for (int j = 0; j < stones.Length; j++) {

            // if another stone has same row or 
            // column as this stone then both lie 
            // in the same component
            if (stones[i][0] == stones[j][0] || stones[i][1] == stones[j][1]) {
                dfs(j, v, stones);
            }
        }
    }

    static int maxRemove(int[][] stones) {
        int n = stones.Length;

        bool[] visited = new bool[n];
        int components = 0;

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

            // visiting the stone if not visited
            // and finding all the stones lying in 
            // the same component as this stone
            if (!visited[i]) {
                dfs(i, visited, stones);
                components++;
            }
        }

        // atleast 1 stone per component 
        // cannot be removed
        return n - components;
    }

//Driver Code Starts

    static void Main() {
        int[][] stones = new int[][] {
            new int[] {0,0},
            new int[] {0,2},
            new int[] {1,1},
            new int[] {2,0},
            new int[] {2,2}
        };
        Console.WriteLine(maxRemove(stones));
    }
}
//Driver Code Ends
JavaScript
function dfs(i, visited, stones) {
    if (visited[i]) return;
    visited[i] = true;

    for (let j = 0; j < stones.length; j++) {
        
        // if another stone has same row or 
        // column as this stone then both lie 
        // in the same component
        if (stones[i][0] === stones[j][0] || stones[i][1] === stones[j][1]) {
            dfs(j, visited, stones);
        }
    }
}

function maxRemove(stones) {
    let n = stones.length;

    let visited = new Array(n).fill(false);
    let components = 0;

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

        // visiting the stone if not visited
        // and finding all the stones lying in 
        // the same component as this stone
        if (!visited[i]) {
            dfs(i, visited, stones);
            components++;
        }
    }

    // atleast 1 stone per component 
    // cannot be removed
    return n - components;
}


//Driver Code Starts
// Driver code
let stones = [[0,0],[0,2],[1,1],[2,0],[2,2]];
console.log(maxRemove(stones));
//Driver Code Ends

Output
3

Time complexity: O(n2) because in the worst case each stone can lie in a unique component, and therefore we make dfs call for each stone, each dfs call takes O(n) because we check if any stone shares the same row or column as current stone.
Auxiliary Space: O(n) for recursive stack and visited array.

Comment