1637. Widest Vertical Area Between Two Points Containing No Points
Problem Description
The given problem requires us to determine the widest vertical gap between any two given points on a 2D plane, with the condition that no points should be inside the area. The input is a list of n
points, where each point is represented as a coordinate pair [x_i, y_i]
. The goal is to find the maximum width between two lines that are parallel to the y-axis such that the lines do not pass through any of the given points.
Intuition
To solve this problem, the main insight is to focus solely on the x-coordinates, since the vertical area extends infinitely along the y-axis. Essentially, we want to find two x-coordinates that have the greatest distance between them without any other x-coordinates lying in between.
The initial step is straightforward: we extract the x-coordinates from the provided points. With this list of x-coordinates alone, we want to identify the largest gap. A brute force approach could involve comparing every pair of x-coordinates to calculate the gap, but this is inefficient as the number of comparisons grows quickly with the number of points (O(n^2)
comparisons).
A key realization is that we can find the largest gap more efficiently. If the x-coordinates were sorted, we could simply iterate through them and measure the gaps between consecutive x-coordinates, because the largest gap must be between two consecutive points in a sorted list. Sorting reduces the problem to a linear pass through the points, leading to a O(n log n)
solution due to the sorting step.
However, the given solution uses a bucketing approach, which is more complex. Bucketing can be a clever optimization when the points are distributed over a large range and we expect to find an even wider vertical area. The idea is to divide the range of x-coordinates into equal-width intervals, or buckets, then place the x-coordinates into the corresponding buckets. We can then find the maximum gap by looking only at the maximum and minimum values in each bucket, and observing the gaps between the max of one bucket and the min of the next non-empty bucket. This approach can achieve better than O(n log n)
performance under certain distributions of input. The complexity of the bucket approach in the solution provided however remains O(n)
because it consists of creating the buckets and then iterating through them, which both happen in linear time.
In the given solution, a bucket size is calculated, and then each x-coordinate is assigned to a bucket. After that, the maximum and minimum x-values in each bucket are used to find the largest gap between consecutive non-empty buckets.
Learn more about Sorting patterns.
Solution Approach
In the provided solution, we seek to find the widest gap between the x-coordinates of the given points without any points lying in that area. Here's how the solution is implemented:
- We start by extracting all the x-coordinates from the given points into a list named
nums
. - The range of x-coordinates is computed by finding the minimum
mi
and maximummx
values innums
. - Instead of sorting all the x-coordinates, we use bucketing to group the values. The bucket size is chosen as the maximum of 1 and the result of the integer division of the range
(mx - mi)
by(n - 1)
, wheren
is the number of points. This determines how many values will be placed in each bucket on average. - The buckets are initialized as a list of pairs
[inf, -inf]
where we will keep the minimum (first element) and maximum (second element) x-coordinate that falls into each bucket. - We iterate over
nums
and assign each x-coordinate to the appropriate bucket. An x-coordinatex
is placed in the bucket at index(x - mi) // bucket_size
. Inside the bucket, we update the minimum or maximum value as necessary. - Once the buckets are populated, we iterate through them to find the maximal gap. We use two variables:
prev
, which keeps track of the rightmost x-coordinate we've seen so far (initialized toinf
), andans
, which will store the solution. - During the bucket iteration, we only consider non-empty buckets, determined by checking if
curmin <= curmax
. For each non-empty bucket, we calculate the gap ascurmin - prev
, which is the distance between the current bucket's leftmost point and the previous bucket's rightmost point. We updateans
with the maximum of itself and the current gap. prev
is then updated tocurmax
, handling the next iteration's calculations.- After going through all buckets,
ans
holds the largest gap found, which is our final answer.
This approach cleverly exploits bucketing to essentially perform a sort of "coarse" sorting, allowing us to bypass the need to sort all the x-coordinates and still find the widest vertical area. It systematically reduces the problem into smaller manageable subproblems that can be solved quickly in linear time.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's consider a small example to illustrate the solution approach. Suppose we are given the following points on a 2D plane: [(4, 10), (2, 6), (12, 8), (8, 5)]
.
-
First, we extract the x-coordinates from the given points:
nums = [4, 2, 12, 8]
. -
We calculate the range by finding the minimum and maximum values in
nums
, which aremi = 2
andmx = 12
. -
Next, we determine the bucket size. The range
(mx - mi) = (12 - 2) = 10
, and we haven = 4
points. So, the bucket size ismax(1, 10 // (4 - 1)) = max(1, 3) = 3
. -
We initialize four buckets with
[inf, -inf]
, because we have(mx - mi) // bucket_size + 1 = (10 // 3) + 1 = 4
buckets.- Buckets:
[[inf, -inf], [inf, -inf], [inf, -inf], [inf, -inf]]
- Buckets:
-
We iterate over
nums
and assign each x-coordinate to the correct bucket:4
goes into bucket0
(since(4 - 2) // 3 = 0
)2
goes into bucket0
(since(2 - 2) // 3 = 0
)12
goes into bucket3
(since(12 - 2) // 3 = 3
)8
goes into bucket2
(since(8 - 2) // 3 = 2
)
After bucketing, we update the buckets with the min and max values:
- Buckets:
[[2, 4], [inf, -inf], [8, 8], [12, 12]]
-
To find the maximum gap, we initialize
prev = 2
(the leftmost x-coordinate) andans = 0
. We iterate through the buckets:- Skip bucket
1
because it is empty ([inf, -inf]
). - At bucket
2
,curmin = 8
,curmax = 8
. The gap is8 - 4 = 4
. Therefore,ans = 4
andprev = 8
. - Skip bucket
3
because it is right next to the previous non-empty bucket.
- Skip bucket
-
After iterating through the buckets,
ans = 4
is the widest vertical gap we can have between two lines that are parallel to the y-axis without any points lying in that region.
This example aligns with the solution's logic, where bucketing greatly simplifies the problem and allows us to avoid sorting the entire list of x-coordinates while still finding the maximum gap efficiently.
Solution Implementation
1from typing import List
2from math import inf
3
4class Solution:
5 def maxWidthOfVerticalArea(self, points: List[List[int]]) -> int:
6 # Extract the x-coordinates from the points
7 x_coords = [x for x, _ in points]
8
9 # Get the number of points
10 num_points = len(x_coords)
11
12 # Find the minimum and maximum x-coordinates
13 min_coord, max_coord = min(x_coords), max(x_coords)
14
15 # Calculate the bucket size based on the range of x-coordinates and number of points
16 bucket_size = max(1, (max_coord - min_coord) // (num_points - 1))
17
18 # Determine the number of buckets required
19 bucket_count = (max_coord - min_coord) // bucket_size + 1
20
21 # Initialize buckets, each will hold the minimum and maximum x-coordinates for that bucket
22 buckets = [[inf, -inf] for _ in range(bucket_count)]
23
24 # Distribute x-coordinates into buckets
25 for x in x_coords:
26 index = (x - min_coord) // bucket_size
27 buckets[index][0] = min(buckets[index][0], x) # Update min for the bucket
28 buckets[index][1] = max(buckets[index][1], x) # Update max for the bucket
29
30 # Variable to keep track of maximum width of vertical area between points
31 max_width = 0
32
33 # Variable to keep track of the previous bucket's max x-coordinate
34 prev_max = inf
35
36 # Calculate the maximum width by considering the gaps between the buckets
37 for bucket_min, bucket_max in buckets:
38 # Ignore empty buckets
39 if bucket_min > bucket_max:
40 continue
41 # Update maximum width if current gap is larger than what we have seen before
42 max_width = max(max_width, bucket_min - prev_max)
43 # Update previous bucket's max for the next iteration
44 prev_max = bucket_max
45
46 # Return the maximum width of vertical area found
47 return max_width
48
49# Example usage:
50# sol = Solution()
51# print(sol.maxWidthOfVerticalArea([[8,7], [9,9], [7,4], [9,7]])) # Should output 1
52
1class Solution {
2 public int maxWidthOfVerticalArea(int[][] points) {
3 int numPoints = points.length; // The total number of points provided
4 int[] xCoords = new int[numPoints]; // Array to hold the x-coordinates of the points
5
6 // Extract the x-coordinates from each point and store them in xCoords
7 for (int i = 0; i < numPoints; ++i) {
8 xCoords[i] = points[i][0];
9 }
10
11 // Define infinity as a large number
12 final int infinity = 1 << 30;
13 // Initialize variables to store the minimum and maximum x-coordinates
14 int minX = infinity, maxX = -infinity;
15
16 // Find the minimum and maximum x-coordinates among all points
17 for (int x : xCoords) {
18 minX = Math.min(minX, x);
19 maxX = Math.max(maxX, x);
20 }
21
22 // Calculate the size of each bucket for the bucket sort algorithm
23 int bucketSize = Math.max(1, (maxX - minX) / (numPoints - 1));
24 int bucketCount = (maxX - minX) / bucketSize + 1; // The number of buckets
25
26 // Create a 2D array to store the minimum and maximum values in each bucket
27 int[][] buckets = new int[bucketCount][2];
28 for (int[] bucket : buckets) {
29 bucket[0] = infinity;
30 bucket[1] = -infinity;
31 }
32
33 // Place each x-coordinate into the correct bucket
34 for (int x : xCoords) {
35 int index = (x - minX) / bucketSize;
36 buckets[index][0] = Math.min(buckets[index][0], x);
37 buckets[index][1] = Math.max(buckets[index][1], x);
38 }
39
40 // Initialize the variable for the previous maximum x-coordinate
41 int prevMaxX = infinity;
42 // Initialize the answer to zero, which represents the maximum width found
43 int maxGap = 0;
44
45 // Iterate through each bucket and calculate the gap between adjacent non-empty buckets
46 for (int[] bucket : buckets) {
47 if (bucket[0] > bucket[1]) { // Skip the empty bucket
48 continue;
49 }
50 // Update the maximum gap found so far
51 maxGap = Math.max(maxGap, bucket[0] - prevMaxX);
52 // Update the previous maximum x-coordinate
53 prevMaxX = bucket[1];
54 }
55 // Return the maximum gap (maximum width of vertical area)
56 return maxGap;
57 }
58}
59
1#include <vector>
2#include <algorithm>
3
4using namespace std;
5
6class Solution {
7public:
8 int maxWidthOfVerticalArea(vector<vector<int>>& points) {
9 int n = points.size();
10 // Create a vector to hold the x-coordinates
11 vector<int> xCoords;
12 for (const auto& point : points) {
13 xCoords.push_back(point[0]);
14 }
15
16 // Set initial maximum and minimum values for coordinates
17 const int INF = 1 << 30;
18 int minX = INF, maxX = -INF;
19
20 // Find minimum and maximum x-coordinates
21 for (int x : xCoords) {
22 minX = min(minX, x);
23 maxX = max(maxX, x);
24 }
25
26 // Calculate the size of each bucket
27 int bucketSize = max(1, (maxX - minX) / (n - 1));
28 int bucketCount = (maxX - minX) / bucketSize + 1;
29
30 // Initialize buckets to hold minimum and maximum values
31 vector<pair<int, int>> buckets(bucketCount, {INF, -INF});
32
33 // Distribute x-coordinates into buckets
34 for (int x : xCoords) {
35 int index = (x - minX) / bucketSize;
36 buckets[index].first = min(buckets[index].first, x);
37 buckets[index].second = max(buckets[index].second, x);
38 }
39
40 int maxWidth = 0;
41 int prevMax = INF;
42
43 // Calculate the maximum width between adjacent vertical lines
44 for (const auto& [bucketMin, bucketMax] : buckets) {
45 if (bucketMin > bucketMax) continue; // Skip empty buckets
46 maxWidth = max(maxWidth, bucketMin - prevMax);
47 prevMax = bucketMax;
48 }
49
50 return maxWidth;
51 }
52};
53
1function maxWidthOfVerticalArea(points: number[][]): number {
2 // Extract the x-coordinates from the points
3 const xCoordinates: number[] = points.map(point => point[0]);
4 const infinity = 1 << 30; // A large value to represent infinity
5 const numPoints = xCoordinates.length;
6 let minCoordinate = infinity; // Initialize minimum coordinate to infinity
7 let maxCoordinate = -infinity; // Initialize maximum coordinate to negative infinity
8
9 // Find the minimum and maximum x-coordinates
10 for (const x of xCoordinates) {
11 minCoordinate = Math.min(minCoordinate, x);
12 maxCoordinate = Math.max(maxCoordinate, x);
13 }
14
15 // Calculate the size of each bucket
16 const bucketSize = Math.max(1, Math.floor((maxCoordinate - minCoordinate) / (numPoints - 1)));
17 const bucketCount = Math.floor((maxCoordinate - minCoordinate) / bucketSize) + 1;
18 // Initialize buckets for the bucket sort
19 const buckets = new Array(bucketCount).fill(0).map(() => [infinity, -infinity]);
20
21 // Distribute the x-coordinates into buckets
22 for (const x of xCoordinates) {
23 const bucketIndex = Math.floor((x - minCoordinate) / bucketSize);
24 buckets[bucketIndex][0] = Math.min(buckets[bucketIndex][0], x);
25 buckets[bucketIndex][1] = Math.max(buckets[bucketIndex][1], x);
26 }
27
28 let previousMax = infinity; // Tracking the maximum value of the previous non-empty bucket
29 let maxDistance = 0; // Initialize the maximum distance to 0
30
31 // Iterate through the buckets to find the maximum distance between non-empty consecutive buckets
32 for (const [leftBoundary, rightBoundary] of buckets) {
33 if (leftBoundary > rightBoundary) {
34 // This means the bucket is empty, move to the next one
35 continue;
36 }
37 maxDistance = Math.max(maxDistance, leftBoundary - previousMax); // Update the maximum distance
38 previousMax = rightBoundary; // Set the previous max to the right boundary of the current bucket
39 }
40
41 // Return the maximum distance found
42 return maxDistance;
43}
44
Time and Space Complexity
The given solution is implementing a form of bucket sort algorithm to find the maximum width of a vertical area between the given points. Let's analyze the time and space complexity:
Time Complexity
- Sorting the X-coordinates: The initial step of extracting the x-coordinates does not sort the values, it merely extracts the x-coordinates into a list, which would take
O(N)
time, whereN
is the number of points. - Finding Min and Max: Finding the minimum and maximum x-coordinate takes
O(N)
since it iterates over all x-coordinates once. - Bucket Calculations: The next several lines calculate the number of buckets and allocate them, which is
O(B)
, whereB
is the number of buckets.B
is calculated based on the range divided by the bucket size, which is(mx - mi) // (n - 1)
. Therefore,B
is at mostN
, since the maximum amount of buckets would be when every unique x-coordinate gets its own bucket. - Buckets Filling: Iterating over the x-coordinates again to fill the buckets also takes
O(N)
, doing constant-time operations to update the bucket min and max. - Determining the Maximum Width: Finally, the last loop to determine the max width goes through each bucket, which takes
O(B)
time. However, the loop skips over the empty buckets effectively, so it processes only the buckets that have been filled. Since the number of meaningful buckets is at mostN - 1
(accounting for the case where all points have unique x-coordinates), the complexity of this step is alsoO(N)
.
So the total time complexity is O(N)
for all the steps since they all operate linearly with respect to the number of points.
Space Complexity
- Storage for x-coordinates: The list of x-coordinates takes
O(N)
space. - Buckets Array: The buckets contribute an additional
O(B)
space complexity. As mentioned earlier, in the worst case,B
can beN
for unique x-coordinates.
Therefore, the total space complexity is also O(N)
.
In conclusion, the time complexity is O(N)
and the space complexity is O(N)
.
Learn more about how to find time and space complexity quickly using problem constraints.
What's the relationship between a tree and a graph?
Recommended Readings
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
LeetCode Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way we
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Want a Structured Path to Master System Design Too? Don’t Miss This!