On a 2D plane, we place n
stones at some integer coordinate points. Each coordinate point may have at most one stone.
A stone can be removed if it shares either the same row or the same column as another stone that has not been removed.
Given an array stones
of length n
where stones[i] = [xi, yi]
represents the location of the ith
stone, return the largest possible number of stones that can be removed.
Example 1:
Input: stones = [[0,0],[0,1],[1,0],[1,2],[2,1],[2,2]] Output: 5 Explanation: One way to remove 5 stones is as follows: 1. Remove stone [2,2] because it shares the same row as [2,1]. 2. Remove stone [2,1] because it shares the same column as [0,1]. 3. Remove stone [1,2] because it shares the same row as [1,0]. 4. Remove stone [1,0] because it shares the same column as [0,0]. 5. Remove stone [0,1] because it shares the same row as [0,0]. Stone [0,0] cannot be removed since it does not share a row/column with another stone still on the plane.
Example 2:
Input: stones = [[0,0],[0,2],[1,1],[2,0],[2,2]] Output: 3 Explanation: One way to make 3 moves is as follows: 1. Remove stone [2,2] because it shares the same row as [2,0]. 2. Remove stone [2,0] because it shares the same column as [0,0]. 3. 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 a row/column with another stone still on the plane.
Example 3:
Input: stones = [[0,0]] Output: 0 Explanation: [0,0] is the only stone on the plane, so you cannot remove it.
Constraints:
1 <= stones.length <= 1000
0 <= xi, yi <= 104
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach is to explore every possible combination of removing stones. We will start by considering each stone and deciding whether to remove it or keep it, then check if the remaining stones satisfy the problem's condition.
Here's how the algorithm would work step-by-step:
def most_stones_removed_brute_force(stones):
number_of_stones = len(stones)
maximum_stones_removed = 0
for i in range(2**number_of_stones):
removed_stones_indices = []
remaining_stones = []
stones_removed_count = 0
for stone_index in range(number_of_stones):
# Check if the stone should be removed based on the current bitmask
if (i >> stone_index) & 1:
removed_stones_indices.append(stone_index)
stones_removed_count += 1
else:
remaining_stones.append(stones[stone_index])
# Check if the remaining stones are connected
if not remaining_stones:
maximum_stones_removed = max(maximum_stones_removed, stones_removed_count)
continue
is_connected = True
for current_stone_index in range(len(remaining_stones)):
stone = remaining_stones[current_stone_index]
found_neighbor = False
for neighbor_stone_index in range(len(remaining_stones)):
if current_stone_index == neighbor_stone_index:
continue
neighbor = remaining_stones[neighbor_stone_index]
if stone[0] == neighbor[0] or stone[1] == neighbor[1]:
found_neighbor = True
break
# If no neighbor is found, the stones are not connected
if not found_neighbor:
is_connected = False
break
# Update max stones removed if remaining stones are connected
if is_connected:
maximum_stones_removed = max(maximum_stones_removed, stones_removed_count)
return maximum_stones_removed
The goal is to find the maximum number of stones we can remove. The key idea is to view stones connected by the same row or column as belonging to a group and then use the principle that in each group, we can remove all stones except for one. By identifying these connected components, we determine the number of removable stones.
Here's how the algorithm would work step-by-step:
def mostStonesRemoved(stones):
number_of_stones = len(stones)
visited = [False] * number_of_stones
number_of_connected_components = 0
def depthFirstSearch(stone_index):
visited[stone_index] = True
for next_stone_index in range(number_of_stones):
if not visited[next_stone_index] and \
(stones[stone_index][0] == stones[next_stone_index][0] or \
stones[stone_index][1] == stones[next_stone_index][1]):
depthFirstSearch(next_stone_index)
# Find all connected components.
for stone_index in range(number_of_stones):
if not visited[stone_index]:
depthFirstSearch(stone_index)
number_of_connected_components += 1
# The maximum number of stones that can be removed.
# This is stones count minus connected components.
return number_of_stones - number_of_connected_components
def removeStones(stones):
# Need to use disjoint set data structure to solve this problem.
number_of_stones = len(stones)
parent = list(range(number_of_stones))
def find(stone_index):
# Path compression to optimize find operation
if parent[stone_index] != stone_index:
parent[stone_index] = find(parent[stone_index])
return parent[stone_index]
def union(stone_index_one, stone_index_two):
root_one = find(stone_index_one)
root_two = find(stone_index_two)
if root_one != root_two:
parent[root_one] = root_two
return 1
return 0
number_of_unions = 0
# Iterate over all pairs of stones
# The crux of the disjoint set strategy.
for stone_index_one in range(number_of_stones):
for stone_index_two in range(stone_index_one + 1, number_of_stones):
# Check if the stones are in the same row or column
if stones[stone_index_one][0] == stones[stone_index_two][0] or stones[stone_index_one][1] == stones[stone_index_two][1]:
# If so, union them
number_of_unions += union(stone_index_one, stone_index_two)
# The number of removable stones is the number of unions that were performed
return number_of_unions
Case | How to Handle |
---|---|
Null or empty input list of stones | Return 0 as no stones can be removed from an empty set. |
Single stone in the input list | Return 0 because a single stone cannot be removed since there are no other stones in the same row or column. |
Maximum number of stones allowed by constraints | The union-find algorithm should handle this efficiently without stack overflow, ensuring reasonable execution time. |
All stones are in the same row or column | The union-find will connect all stones together, and the result will be n-1. |
Duplicate stone coordinates (same row and column) | The algorithm should treat these as a single stone, effectively ignoring the duplicates due to the nature of the problem. |
Stones form disconnected components; no two stones share a row or column | The union-find algorithm will not connect any stones, and the return value will be 0. |
Integer overflow when calculating hash keys for coordinates | Use long or appropriate data type to prevent overflow when combining row and column values into a unique ID. |
Stones form a cycle (e.g., stone A connects to B, B to C, and C to A) | The union-find algorithm correctly handles cycles by merging the sets of the connected components. |