Each ship is located at an integer point on the sea represented by a cartesian plane, and each ship is surrounded by an impenetrable rectangular hull. We can only know the region each ship is located at, but we cannot know the exact position of the ship inside the hull.
Given the two integer arrays topRight and bottomLeft representing the top-right and bottom-left coordinates of the area we want to search, return the number of ships present in that area.
You may assume that:
0 <= bottomLeft[0] <= topRight[0] <= 2000 and 0 <= bottomLeft[1] <= topRight[1] <= 2000.Implement the function int countShips(int[][] topRight, int[][] bottomLeft) that takes the top-right and bottom-left coordinates of the area to search and returns the number of ships present in that area.
Notice that assignment will be checked on the total number of calls to your API, so please think carefully about whether to make too many calls. You are not allowed to call the API more than 400 times per test case.
Example 1:
Input: ships = [[1,1],[2,2],[2,0],[1,0]], topRight = [2,2], bottomLeft = [0,0] Output: 3 Explanation: From the example above, the ships are located at [[1,1],[2,2],[2,0],[1,0]]. Using the function countShips(topRight = [2,2], bottomLeft = [0,0]) will return 3 since there are three ships found in the defined area.
Example 2:
Input: ships = [[1,1],[2,2],[2,0],[1,0]], topRight = [2,2], bottomLeft = [2,1] Output: 1 Explanation: Using the function countShips(topRight = [2,2], bottomLeft = [2,1]) will return 1 since there is only one ship found in the defined area.
Constraints:
ships.length <= 10000 <= ships[i][0], ships[i][1] <= 2000topRight.length == 2bottomLeft.length == 20 <= bottomLeft[0] <= topRight[0] <= 20000 <= bottomLeft[1] <= topRight[1] <= 2000When 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 most straightforward way to find all the ships is to meticulously check every single possible spot within the rectangular sea. We will systematically go through the entire area, point by point, and for each one, we'll ask if a ship is located right there.
Here's how the algorithm would work step-by-step:
class Solution(object):
def countShips(self, sea: 'Sea', topRight: 'Point', bottomLeft: 'Point') -> int:
total_ships_found = 0
# Iterate through every single coordinate in the rectangle to ensure no potential ship location is missed.
for current_x_coordinate in range(bottomLeft.x, topRight.x + 1):
for current_y_coordinate in range(bottomLeft.y, topRight.y + 1):
# Create a tiny 1x1 rectangle for the current point to isolate it for the ship check.
current_point = Point(current_x_coordinate, current_y_coordinate)
if sea.hasShips(current_point, current_point):
# A positive result for a single point confirms a unique ship is at these coordinates.
total_ships_found += 1
return total_ships_foundInstead of checking every single point on the map, which would be very slow, this approach works by checking large areas at once. If a large area has no ships, we can ignore it entirely and save a lot of time, only focusing our search on the regions that we know contain ships.
Here's how the algorithm would work step-by-step:
class Solution(object):
def countShips(self, sea: 'Sea', topRight: 'Point', bottomLeft: 'Point') -> int:
# If the API confirms no ships are in this rectangle, we can prune this entire search branch.
if not sea.hasShips(topRight, bottomLeft):
return 0
# If the search area has been reduced to a single point that contains ships, we've found one.
if topRight.x == bottomLeft.x and topRight.y == bottomLeft.y:
return 1
mid_coordinate_x = (bottomLeft.x + topRight.x) // 2
mid_coordinate_y = (bottomLeft.y + topRight.y) // 2
# To pinpoint ships, we divide the rectangle and recursively search the four resulting quadrants.
top_left_quadrant_ships = self.countShips(sea, Point(mid_coordinate_x, topRight.y), Point(bottomLeft.x, mid_coordinate_y + 1))
top_right_quadrant_ships = self.countShips(sea, topRight, Point(mid_coordinate_x + 1, mid_coordinate_y + 1))
bottom_left_quadrant_ships = self.countShips(sea, Point(mid_coordinate_x, mid_coordinate_y), bottomLeft)
bottom_right_quadrant_ships = self.countShips(sea, Point(topRight.x, mid_coordinate_y), Point(mid_coordinate_x + 1, bottomLeft.y))
return top_left_quadrant_ships + top_right_quadrant_ships + bottom_left_quadrant_ships + bottom_right_quadrant_ships| Case | How to Handle |
|---|---|
| The query rectangle is a single point, where topRight equals bottomLeft. | The base case of the recursion handles this by making one hasShips call and returning 1 or 0 accordingly. |
| The query rectangle contains no ships at all. | The solution should make a single hasShips call on the initial rectangle, which returns false, and then immediately returns 0. |
| Ships are located exactly on the boundaries used for splitting the rectangle into quadrants. | The splitting logic must be precise, assigning each point on a boundary to exactly one sub-rectangle to prevent missing ships or double counting. |
| The query rectangle is a one-dimensional line, either horizontal or vertical. | The 2D divide-and-conquer approach naturally handles this, as two of the four recursive sub-quadrants will always be invalid and terminate immediately. |
| All ships in the query rectangle are clustered in a single point or a very small area. | The recursion quickly prunes the three empty quadrants at each level, resulting in an efficient search with a low number of API calls. |
| Ships are maximally dispersed, for example, at the corners of a large rectangle. | This represents a worst-case scenario for API calls where recursion proceeds down multiple branches, severely testing the solution's efficiency against the API call limit. |
| A recursive call is made with an invalid rectangle where a bottomLeft coordinate is greater than a topRight coordinate. | The recursive function must have a base case to check for and handle these invalid rectangles by returning 0 to correctly terminate that search path. |
| The query rectangle is the largest possible area, from [0,0] to [2000,2000]. | The solution's scalability is tested as recursion will reach its maximum depth, requiring efficient pruning to stay within the API call limit. |