3102. Minimize Manhattan Distances
Problem Description
You are given an array points
representing integer coordinates of some points on a 2D plane, where points[i] = [x_i, y_i]
.
The distance between two points is defined as their Manhattan distance.
Return the minimum possible value for the maximum distance between any two points by removing exactly one point.
Intuition
The task is to minimize the maximum Manhattan distance between any two points after removing one point. The Manhattan distance between two points (x_1, y_1)
and (x_2, y_2)
can be expressed in different ways, but with some transformation, it can be simplified to:
max((x_1 + y_1) - (x_2 + y_2), (x_2 + y_2) - (x_1 + y_1), (x_1 - y_1) - (x_2 - y_2), (x_2 - y_2) - (x_1 - y_1))
.
This simplification highlights that the Manhattan distance can be broken into two components:
- The maximum range of
x + y
- The maximum range of
x - y
By storing the values of x + y
and x - y
for all points in two separate ordered sets, we can efficiently calculate the maximum and minimum values of these quantities. For each point, remove its contribution from the sets, calculate the new potential maximum distance when this point is omitted, and update the answer accordingly. Finally, return the smallest maximum distance obtained.
Solution Approach
To solve the problem, we use an efficient approach leveraging the properties of the SortedList
from the sortedcontainers
module in Python. This data structure allows for logarithmic time complexity to insert and delete elements while maintaining order.
Here's a step-by-step breakdown of the solution:
-
Initialize Sorted Lists:
- Create two
SortedList
instances,sl1
andsl2
, to keep track of thex + y
andx - y
values of each point, respectively.
- Create two
-
Populate Sorted Lists:
- Iterate through each point
(x, y)
in the input listpoints
. - Compute
x + y
andx - y
for each point and add these values tosl1
andsl2
.
- Iterate through each point
-
Compute Minimum Maximum Distance:
- Initialize a variable
ans
to store the result, starting withinf
. - For each point
(x, y)
:- Remove the point's
x + y
andx - y
values fromsl1
andsl2
. - Calculate the maximum range of
x + y
andx - y
by taking the difference between the maximum and minimum elements insl1
andsl2
. - The current maximum Manhattan distance is given by the larger of these two ranges.
- Update
ans
with the minimum of itself and the current maximum distance. - Add the removed values back to
sl1
andsl2
to restore them for the next iteration.
- Remove the point's
- Initialize a variable
-
Return the Result:
- After processing all points,
ans
will contain the smallest possible value of the maximum distance when one point is removed.
- After processing all points,
The key aspect of this solution is efficiently calculating and updating the potential maximum distances after removing each point, leveraging the properties of ordered sets provided by SortedList
.
class Solution:
def minimumDistance(self, points: List[List[int]]) -> int:
sl1 = SortedList()
sl2 = SortedList()
for x, y in points:
sl1.add(x + y)
sl2.add(x - y)
ans = inf
for x, y in points:
sl1.remove(x + y)
sl2.remove(x - y)
ans = min(ans, max(sl1[-1] - sl1[0], sl2[-1] - sl2[0]))
sl1.add(x + y)
sl2.add(x - y)
return ans
This algorithm efficiently exploits the mathematical transformation of the problem and the properties of SortedList
to achieve the desired result efficiently.
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 points = [[1, 2], [3, 4], [5, 6], [7, 8]]
and apply the solution approach step-by-step:
-
Initialize Sorted Lists:
- Create two
SortedList
instances:sl1
for storingx + y
values andsl2
for storingx - y
values.
- Create two
-
Populate Sorted Lists:
- Compute
x + y
andx - y
for each point and add them tosl1
andsl2
.
Points: [1, 2] [3, 4] [5, 6] [7, 8] x + y values: 3 7 11 15 x - y values: -1 -1 -1 -1
sl1
contains[3, 7, 11, 15]
sl2
contains[-1, -1, -1, -1]
- Compute
-
Compute Minimum Maximum Distance:
-
Initialize
ans
withinf
. -
Iterate over each point, calculate maximum Manhattan distances, and update
ans
:
Iteration 1: Remove point
[1, 2]
:-
Remove
3
fromsl1
and-1
fromsl2
. -
sl1
now:[7, 11, 15]
-
sl2
now:[-1, -1, -1]
-
Calculate maximum distance:
max(sl1[-1] - sl1[0], sl2[-1] - sl2[0]) = max(15 - 7, 0) = 8
-
Update
ans = min(ans, 8) → ans = 8
-
Add
3
and-1
back.
Iteration 2: Remove point
[3, 4]
:-
Remove
7
fromsl1
and-1
fromsl2
. -
sl1
now:[3, 11, 15]
-
sl2
now:[-1, -1, -1]
-
Calculate maximum distance:
- Max distance remains
max(15 - 3, 0) = 12
- Max distance remains
-
ans = min(ans, 12) → ans = 8
-
Add
7
and-1
back.
Iteration 3: Remove point
[5, 6]
:-
Remove
11
fromsl1
and-1
fromsl2
. -
sl1
now:[3, 7, 15]
-
sl2
now:[-1, -1, -1]
-
Calculate maximum distance:
- Max distance remains
max(15 - 3, 0) = 12
- Max distance remains
-
ans = min(ans, 12) → ans = 8
-
Add
11
and-1
back.
Iteration 4: Remove point
[7, 8]
:-
Remove
15
fromsl1
and-1
fromsl2
. -
sl1
now:[3, 7, 11]
-
sl2
now:[-1, -1, -1]
-
Calculate maximum distance:
- Max distance remains
max(11 - 3, 0) = 8
- Max distance remains
-
ans = min(ans, 8) → ans = 8
-
Add
15
and-1
back.
-
-
Return the Result:
The smallest possible value of the maximum distance when one point is removed is
8
.
Solution Implementation
1from typing import List
2from sortedcontainers import SortedList
3from math import inf
4
5class Solution:
6 def minimumDistance(self, points: List[List[int]]) -> int:
7 sl1 = SortedList() # Initialize a sorted list for x + y
8 sl2 = SortedList() # Initialize a sorted list for x - y
9
10 # Populate the sorted lists with x + y and x - y values from the points
11 for x, y in points:
12 sl1.add(x + y)
13 sl2.add(x - y)
14
15 # Initialize answer with infinity
16 ans = inf
17
18 # Iterate over each point to check minimum distance
19 for x, y in points:
20 # Remove current point's x + y and x - y to find max distance without it
21 sl1.remove(x + y)
22 sl2.remove(x - y)
23
24 # Calculate the max distance between the extremities of the sorted lists
25 current_distance = max(sl1[-1] - sl1[0], sl2[-1] - sl2[0])
26 # Update answer with the minimum distance found
27 ans = min(ans, current_distance)
28
29 # Re-add the current point's x + y and x - y for further iterations
30 sl1.add(x + y)
31 sl2.add(x - y)
32
33 return ans
34
1import java.util.TreeMap;
2
3class Solution {
4 public int minimumDistance(int[][] points) {
5 // TreeMap to store the combined coordinates x+y and their count
6 TreeMap<Integer, Integer> sumMap = new TreeMap<>();
7 // TreeMap to store the difference coordinates x-y and their count
8 TreeMap<Integer, Integer> diffMap = new TreeMap<>();
9
10 // Populate the TreeMaps with the respective coordinate transformations
11 for (int[] point : points) {
12 int x = point[0];
13 int y = point[1];
14 // Increment the count for x+y in sumMap
15 sumMap.merge(x + y, 1, Integer::sum);
16 // Increment the count for x-y in diffMap
17 diffMap.merge(x - y, 1, Integer::sum);
18 }
19
20 // Initialize answer with max integer value
21 int ans = Integer.MAX_VALUE;
22
23 // Iterate over each point to calculate the potential minimum distance
24 for (int[] point : points) {
25 int x = point[0];
26 int y = point[1];
27
28 // Decrement the count of x+y in sumMap, remove if count goes to zero
29 if (sumMap.merge(x + y, -1, Integer::sum) == 0) {
30 sumMap.remove(x + y);
31 }
32
33 // Decrement the count of x-y in diffMap, remove if count goes to zero
34 if (diffMap.merge(x - y, -1, Integer::sum) == 0) {
35 diffMap.remove(x - y);
36 }
37
38 // Calculate the minimum of the current ans or max difference between keys in TreeMaps
39 ans = Math.min(
40 ans, Math.max(sumMap.lastKey() - sumMap.firstKey(), diffMap.lastKey() - diffMap.firstKey())
41 );
42
43 // Restore the count of x+y in sumMap
44 sumMap.merge(x + y, 1, Integer::sum);
45 // Restore the count of x-y in diffMap
46 diffMap.merge(x - y, 1, Integer::sum);
47 }
48
49 // Return the minimum distance found
50 return ans;
51 }
52}
53
1class Solution {
2public:
3 int minimumDistance(vector<vector<int>>& points) {
4 // Use two multiset containers to keep track of transformed points
5 std::multiset<int> set1;
6 std::multiset<int> set2;
7
8 // Transform each point and insert into the multisets
9 for (auto& point : points) {
10 int x = point[0], y = point[1];
11 set1.insert(x + y); // Store the sum of x and y
12 set2.insert(x - y); // Store the difference of x and y
13 }
14
15 // Initialize the minimum answer with a large value
16 int answer = INT_MAX;
17
18 // Iterate over each point
19 for (auto& point : points) {
20 int x = point[0], y = point[1];
21
22 // Remove the current transformed values from the multisets
23 set1.erase(set1.find(x + y));
24 set2.erase(set2.find(x - y));
25
26 // Calculate the new potential minimum distance
27 int maxDiff = std::max(*set1.rbegin() - *set1.begin(), *set2.rbegin() - *set2.begin());
28 answer = std::min(answer, maxDiff);
29
30 // Restore the transformed values back to the multisets
31 set1.insert(x + y);
32 set2.insert(x - y);
33 }
34
35 // Return the minimum possible distance found
36 return answer;
37 }
38};
39
1// Type used for values and results in comparison functions
2type CompareFunction<T, R extends 'number' | 'boolean'> = (a: T, b: T) => R extends 'number' ? number : boolean;
3
4// Node structure for Treap
5interface TreapNode<T> {
6 value: T; // Value stored in the node
7 count: number; // Count of duplicates
8 size: number; // Size of the subtree rooted at this node
9 priority: number; // Priority for balancing treap
10 left: TreapNode<T> | null; // Left child
11 right: TreapNode<T> | null; // Right child
12}
13
14// Initialize a new node
15function createTreapNode<T>(value: T): TreapNode<T> {
16 return {
17 value,
18 count: 1,
19 size: 1,
20 priority: Math.random(),
21 left: null,
22 right: null
23 };
24}
25
26// Update size property of the node
27function pushUp(node: TreapNode<any>): void {
28 node.size = node.count + getSize(node.left) + getSize(node.right);
29}
30
31// Rotate the subtree right
32function rotateRight<T>(node: TreapNode<T>): TreapNode<T> {
33 const left = node.left!;
34 node.left = left.right;
35 left.right = node;
36 pushUp(node);
37 pushUp(left);
38 return left;
39}
40
41// Rotate the subtree left
42function rotateLeft<T>(node: TreapNode<T>): TreapNode<T> {
43 const right = node.right!;
44 node.right = right.left;
45 right.left = node;
46 pushUp(node);
47 pushUp(right);
48 return right;
49}
50
51// Get size of the node
52function getSize(node: TreapNode<any> | null): number {
53 return node ? node.size : 0;
54}
55
56// Get priority factor of the node
57function getFac(node: TreapNode<any> | null): number {
58 return node ? node.priority : 0;
59}
60
61// TreapMultiSet global variables
62let root: TreapNode<number> = createTreapNode(Infinity);
63let compareFn: CompareFunction<number, 'number'> = (a, b) => a - b;
64let leftBound: number = -Infinity;
65let rightBound: number = Infinity;
66
67// Initialize for new treap
68function initTreap(compareFnInit?: CompareFunction<number, 'number'>, leftBoundInit?: number, rightBoundInit?: number): void {
69 compareFn = compareFnInit || ((a, b) => a - b);
70 leftBound = leftBoundInit !== undefined ? leftBoundInit : -Infinity;
71 rightBound = rightBoundInit !== undefined ? rightBoundInit : Infinity;
72 root = createTreapNode(rightBound);
73 root.priority = Infinity;
74 root.left = createTreapNode(leftBound);
75 root.left.priority = -Infinity;
76 pushUp(root);
77}
78
79// Add values to the treap
80function add(...values: number[]): void {
81 for (const value of values) {
82 root.left = addNode(root.left, value, root, 'left');
83 }
84}
85
86// Helper function to recursively add a node
87function addNode(node: TreapNode<number> | null, value: number, parent: TreapNode<number>, direction: 'left' | 'right'): TreapNode<number> | null {
88 if (node === null) {
89 node = createTreapNode(value);
90 } else if (compareFn(node.value, value) === 0) {
91 node.count++;
92 } else if (compareFn(node.value, value) > 0) {
93 node.left = addNode(node.left, value, node, 'left');
94 if (getFac(node.left) > node.priority) {
95 return rotateRight(node);
96 }
97 } else {
98 node.right = addNode(node.right, value, node, 'right');
99 if (getFac(node.right) > node.priority) {
100 return rotateLeft(node);
101 }
102 }
103 pushUp(node);
104 return node;
105}
106
107// Remove value from the treap
108function deleteValue(value: number): void {
109 root.left = deleteNode(root.left, value, root, 'left');
110}
111
112// Helper function to recursively remove a node
113function deleteNode(node: TreapNode<number> | null, value: number, parent: TreapNode<number>, direction: 'left' | 'right'): TreapNode<number> | null {
114 if (node === null) return null;
115
116 if (compareFn(node.value, value) === 0) {
117 if (node.count > 1) {
118 node.count--;
119 } else if (node.left == null || node.right == null) {
120 return node.left ?? node.right;
121 } else if (getFac(node.left) > getFac(node.right)) {
122 parent[direction] = rotateRight(node);
123 return deleteNode(parent[direction]?.right, value, parent[direction]!, 'right');
124 } else {
125 parent[direction] = rotateLeft(node);
126 return deleteNode(parent[direction]?.left, value, parent[direction]!, 'left');
127 }
128 } else if (compareFn(node.value, value) > 0) {
129 node.left = deleteNode(node.left, value, node, 'left');
130 } else {
131 node.right = deleteNode(node.right, value, node, 'right');
132 }
133 pushUp(node);
134 return node;
135}
136
137// Find and return the smallest value in the treap
138function first(): number | undefined {
139 let node = root;
140 while (node.left && node.left.value !== leftBound) {
141 node = node.left;
142 }
143 return node.left ? node.left.value : undefined;
144}
145
146// Find and return the largest value in the treap
147function last(): number | undefined {
148 let node = root;
149 while (node.right && node.right.value !== rightBound) {
150 node = node.right;
151 }
152 return node.value !== rightBound ? node.value : undefined;
153}
154
155// Calculate the minimum distance based on input points
156function minimumDistance(points: number[][]): number {
157 initTreap();
158 const st1 = new TreapMultiSet<number>();
159 const st2 = new TreapMultiSet<number>();
160 for (const [x, y] of points) {
161 add(x + y);
162 add(x - y);
163 }
164 let ans = Infinity;
165 for (const [x, y] of points) {
166 deleteValue(x + y);
167 deleteValue(x - y);
168 ans = Math.min(ans, Math.max(last()! - first()!, last()! - first()!));
169 add(x + y);
170 add(x - y);
171 }
172 return ans;
173}
174
Time and Space Complexity
The time complexity of the code is O(n \log n)
because the operations that dominate this complexity are the insertions and deletions from the SortedList
, both of which take O(\log n)
time. The loops iterate over all points and each loop iteration involves a fixed number of SortedList
operations.
The space complexity of the code is O(n)
, where n
is the number of points, since the two SortedList
instances sl1
and sl2
store n
elements each.
Learn more about how to find time and space complexity quickly.
You are given an array of intervals where intervals[i] = [start_i, end_i]
represent the start and end of the ith
interval. You need to merge all overlapping intervals and return an array of the non-overlapping intervals that cover all the intervals in the input.
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
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
Coding Interview 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
Want a Structured Path to Master System Design Too? Don’t Miss This!