# 3102. Minimize Manhattan Distances

## Description

You are given a array points representing integer coordinates of some points on a 2D plane, where points[i] = [xi, yi].

The distance between two points is defined as their Manhattan distance.

Return the minimum possible value for maximum distance between any two points by removing exactly one point.

Example 1:

Input: points = [[3,10],[5,15],[10,2],[4,4]]

Output: 12

Explanation:

The maximum distance after removing each point is the following:

• After removing the 0th point the maximum distance is between points (5, 15) and (10, 2), which is \|5 - 10\| + \|15 - 2\| = 18.
• After removing the 1st point the maximum distance is between points (3, 10) and (10, 2), which is \|3 - 10\| + \|10 - 2\| = 15.
• After removing the 2nd point the maximum distance is between points (5, 15) and (4, 4), which is \|5 - 4\| + \|15 - 4\| = 12.
• After removing the 3rd point the maximum distance is between points (5, 15) and (10, 2), which is \|5 - 10\| + \|15 - 2\| = 18.

12 is the minimum possible maximum distance between any two points after removing exactly one point.

Example 2:

Input: points = [[1,1],[1,1],[1,1]]

Output: 0

Explanation:

Removing any of the points results in the maximum distance between any two points of 0.

Constraints:

• 3 <= points.length <= 105
• points[i].length == 2
• 1 <= points[i][0], points[i][1] <= 108

## Solutions

### Solution 1

• class Solution {
public int minimumDistance(int[][] points) {
TreeMap<Integer, Integer> tm1 = new TreeMap<>();
TreeMap<Integer, Integer> tm2 = new TreeMap<>();
for (int[] p : points) {
int x = p[0], y = p[1];
tm1.merge(x + y, 1, Integer::sum);
tm2.merge(x - y, 1, Integer::sum);
}
int ans = Integer.MAX_VALUE;
for (int[] p : points) {
int x = p[0], y = p[1];
if (tm1.merge(x + y, -1, Integer::sum) == 0) {
tm1.remove(x + y);
}
if (tm2.merge(x - y, -1, Integer::sum) == 0) {
tm2.remove(x - y);
}
ans = Math.min(
ans, Math.max(tm1.lastKey() - tm1.firstKey(), tm2.lastKey() - tm2.firstKey()));
tm1.merge(x + y, 1, Integer::sum);
tm2.merge(x - y, 1, Integer::sum);
}
return ans;
}
}

• class Solution {
public:
int minimumDistance(vector<vector<int>>& points) {
multiset<int> st1;
multiset<int> st2;
for (auto& p : points) {
int x = p[0], y = p[1];
st1.insert(x + y);
st2.insert(x - y);
}
int ans = INT_MAX;
for (auto& p : points) {
int x = p[0], y = p[1];
st1.erase(st1.find(x + y));
st2.erase(st2.find(x - y));
ans = min(ans, max(*st1.rbegin() - *st1.begin(), *st2.rbegin() - *st2.begin()));
st1.insert(x + y);
st2.insert(x - y);
}
return ans;
}
};

• from sortedcontainers import SortedList

class Solution:
def minimumDistance(self, points: List[List[int]]) -> int:
sl1 = SortedList()
sl2 = SortedList()
for x, y in points:
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]))
return ans


• func minimumDistance(points [][]int) int {
st1 := redblacktree.New[int, int]()
st2 := redblacktree.New[int, int]()
merge := func(st *redblacktree.Tree[int, int], x, v int) {
c, _ := st.Get(x)
if c+v == 0 {
st.Remove(x)
} else {
st.Put(x, c+v)
}
}
for _, p := range points {
x, y := p[0], p[1]
merge(st1, x+y, 1)
merge(st2, x-y, 1)
}
ans := math.MaxInt
for _, p := range points {
x, y := p[0], p[1]
merge(st1, x+y, -1)
merge(st2, x-y, -1)
ans = min(ans, max(st1.Right().Key-st1.Left().Key, st2.Right().Key-st2.Left().Key))
merge(st1, x+y, 1)
merge(st2, x-y, 1)
}
return ans
}

• function minimumDistance(points: number[][]): number {
const st1 = new TreapMultiSet<number>();
const st2 = new TreapMultiSet<number>();
for (const [x, y] of points) {
}
let ans = Infinity;
for (const [x, y] of points) {
st1.delete(x + y);
st2.delete(x - y);
ans = Math.min(ans, Math.max(st1.last() - st1.first(), st2.last() - st2.first()));
}
return ans;
}

type CompareFunction<T, R extends 'number' | 'boolean'> = (
a: T,
b: T,
) => R extends 'number' ? number : boolean;

interface ITreapMultiSet<T> extends Iterable<T> {
has: (value: T) => boolean;
delete: (value: T) => void;

bisectLeft: (value: T) => number;
bisectRight: (value: T) => number;

indexOf: (value: T) => number;
lastIndexOf: (value: T) => number;

at: (index: number) => T | undefined;
first: () => T | undefined;
last: () => T | undefined;

lower: (value: T) => T | undefined;
higher: (value: T) => T | undefined;
floor: (value: T) => T | undefined;
ceil: (value: T) => T | undefined;

shift: () => T | undefined;
pop: (index?: number) => T | undefined;

count: (value: T) => number;

keys: () => IterableIterator<T>;
values: () => IterableIterator<T>;
rvalues: () => IterableIterator<T>;
entries: () => IterableIterator<[number, T]>;

}

class TreapNode<T = number> {
value: T;
count: number;
size: number;
priority: number;
left: TreapNode<T> | null;
right: TreapNode<T> | null;

constructor(value: T) {
this.value = value;
this.count = 1;
this.size = 1;
this.priority = Math.random();
this.left = null;
this.right = null;
}

static getSize(node: TreapNode<any> | null): number {
return node?.size ?? 0;
}

static getFac(node: TreapNode<any> | null): number {
return node?.priority ?? 0;
}

pushUp(): void {
let tmp = this.count;
tmp += TreapNode.getSize(this.left);
tmp += TreapNode.getSize(this.right);
this.size = tmp;
}

rotateRight(): TreapNode<T> {
// eslint-disable-next-line @typescript-eslint/no-this-alias
let node: TreapNode<T> = this;
const left = node.left;
node.left = left?.right ?? null;
left && (left.right = node);
left && (node = left);
node.right?.pushUp();
node.pushUp();
return node;
}

rotateLeft(): TreapNode<T> {
// eslint-disable-next-line @typescript-eslint/no-this-alias
let node: TreapNode<T> = this;
const right = node.right;
node.right = right?.left ?? null;
right && (right.left = node);
right && (node = right);
node.left?.pushUp();
node.pushUp();
return node;
}
}

class TreapMultiSet<T = number> implements ITreapMultiSet<T> {

constructor(compareFn?: CompareFunction<T, 'number'>);
constructor(compareFn: CompareFunction<T, 'number'>, leftBound: T, rightBound: T);
constructor(
compareFn: CompareFunction<T, any> = (a: any, b: any) => a - b,
leftBound: any = -Infinity,
rightBound: any = Infinity,
) {
this.root = new TreapNode<T>(rightBound);
this.root.priority = Infinity;
this.root.left = new TreapNode<T>(leftBound);
this.root.left.priority = -Infinity;
this.root.pushUp();

this.leftBound = leftBound;
this.rightBound = rightBound;
this.compareFn = compareFn;
}

get size(): number {
return this.root.size - 2;
}

get height(): number {
const getHeight = (node: TreapNode<T> | null): number => {
if (node == null) return 0;
return 1 + Math.max(getHeight(node.left), getHeight(node.right));
};

return getHeight(this.root);
}

/**
*
* @complexity O(logn)
* @description Returns true if value is a member.
*/
has(value: T): boolean {
const compare = this.compareFn;
const dfs = (node: TreapNode<T> | null, value: T): boolean => {
if (node == null) return false;
if (compare(node.value, value) === 0) return true;
if (compare(node.value, value) < 0) return dfs(node.right, value);
return dfs(node.left, value);
};

return dfs(this.root, value);
}

/**
*
* @complexity O(logn)
* @description Add value to sorted set.
*/
const compare = this.compareFn;
const dfs = (
node: TreapNode<T> | null,
value: T,
parent: TreapNode<T>,
direction: 'left' | 'right',
): void => {
if (node == null) return;
if (compare(node.value, value) === 0) {
node.count++;
node.pushUp();
} else if (compare(node.value, value) > 0) {
if (node.left) {
dfs(node.left, value, node, 'left');
} else {
node.left = new TreapNode(value);
node.pushUp();
}

if (TreapNode.getFac(node.left) > node.priority) {
parent[direction] = node.rotateRight();
}
} else if (compare(node.value, value) < 0) {
if (node.right) {
dfs(node.right, value, node, 'right');
} else {
node.right = new TreapNode(value);
node.pushUp();
}

if (TreapNode.getFac(node.right) > node.priority) {
parent[direction] = node.rotateLeft();
}
}
parent.pushUp();
};

values.forEach(value => dfs(this.root.left, value, this.root, 'left'));
return this;
}

/**
*
* @complexity O(logn)
* @description Remove value from sorted set if it is a member.
* If value is not a member, do nothing.
*/
delete(value: T): void {
const compare = this.compareFn;
const dfs = (
node: TreapNode<T> | null,
value: T,
parent: TreapNode<T>,
direction: 'left' | 'right',
): void => {
if (node == null) return;

if (compare(node.value, value) === 0) {
if (node.count > 1) {
node.count--;
node?.pushUp();
} else if (node.left == null && node.right == null) {
parent[direction] = null;
} else {
// 旋到根节点
if (
node.right == null ||
TreapNode.getFac(node.left) > TreapNode.getFac(node.right)
) {
parent[direction] = node.rotateRight();
dfs(parent[direction]?.right ?? null, value, parent[direction]!, 'right');
} else {
parent[direction] = node.rotateLeft();
dfs(parent[direction]?.left ?? null, value, parent[direction]!, 'left');
}
}
} else if (compare(node.value, value) > 0) {
dfs(node.left, value, node, 'left');
} else if (compare(node.value, value) < 0) {
dfs(node.right, value, node, 'right');
}

parent?.pushUp();
};

dfs(this.root.left, value, this.root, 'left');
}

/**
*
* @complexity O(logn)
* @description Returns an index to insert value in the sorted set.
* If the value is already present, the insertion point will be before (to the left of) any existing values.
*/
bisectLeft(value: T): number {
const compare = this.compareFn;
const dfs = (node: TreapNode<T> | null, value: T): number => {
if (node == null) return 0;

if (compare(node.value, value) === 0) {
return TreapNode.getSize(node.left);
} else if (compare(node.value, value) > 0) {
return dfs(node.left, value);
} else if (compare(node.value, value) < 0) {
return dfs(node.right, value) + TreapNode.getSize(node.left) + node.count;
}

return 0;
};

return dfs(this.root, value) - 1;
}

/**
*
* @complexity O(logn)
* @description Returns an index to insert value in the sorted set.
* If the value is already present, the insertion point will be before (to the right of) any existing values.
*/
bisectRight(value: T): number {
const compare = this.compareFn;
const dfs = (node: TreapNode<T> | null, value: T): number => {
if (node == null) return 0;

if (compare(node.value, value) === 0) {
return TreapNode.getSize(node.left) + node.count;
} else if (compare(node.value, value) > 0) {
return dfs(node.left, value);
} else if (compare(node.value, value) < 0) {
return dfs(node.right, value) + TreapNode.getSize(node.left) + node.count;
}

return 0;
};
return dfs(this.root, value) - 1;
}

/**
*
* @complexity O(logn)
* @description Returns the index of the first occurrence of a value in the set, or -1 if it is not present.
*/
indexOf(value: T): number {
const compare = this.compareFn;
let isExist = false;

const dfs = (node: TreapNode<T> | null, value: T): number => {
if (node == null) return 0;

if (compare(node.value, value) === 0) {
isExist = true;
return TreapNode.getSize(node.left);
} else if (compare(node.value, value) > 0) {
return dfs(node.left, value);
} else if (compare(node.value, value) < 0) {
return dfs(node.right, value) + TreapNode.getSize(node.left) + node.count;
}

return 0;
};
const res = dfs(this.root, value) - 1;
return isExist ? res : -1;
}

/**
*
* @complexity O(logn)
* @description Returns the index of the last occurrence of a value in the set, or -1 if it is not present.
*/
lastIndexOf(value: T): number {
const compare = this.compareFn;
let isExist = false;

const dfs = (node: TreapNode<T> | null, value: T): number => {
if (node == null) return 0;

if (compare(node.value, value) === 0) {
isExist = true;
return TreapNode.getSize(node.left) + node.count - 1;
} else if (compare(node.value, value) > 0) {
return dfs(node.left, value);
} else if (compare(node.value, value) < 0) {
return dfs(node.right, value) + TreapNode.getSize(node.left) + node.count;
}

return 0;
};

const res = dfs(this.root, value) - 1;
return isExist ? res : -1;
}

/**
*
* @complexity O(logn)
* @description Returns the item located at the specified index.
* @param index The zero-based index of the desired code unit. A negative index will count back from the last item.
*/
at(index: number): T | undefined {
if (index < 0) index += this.size;
if (index < 0 || index >= this.size) return undefined;

const dfs = (node: TreapNode<T> | null, rank: number): T | undefined => {
if (node == null) return undefined;

if (TreapNode.getSize(node.left) >= rank) {
return dfs(node.left, rank);
} else if (TreapNode.getSize(node.left) + node.count >= rank) {
return node.value;
} else {
return dfs(node.right, rank - TreapNode.getSize(node.left) - node.count);
}
};

const res = dfs(this.root, index + 2);
return ([this.leftBound, this.rightBound] as any[]).includes(res) ? undefined : res;
}

/**
*
* @complexity O(logn)
* @description Find and return the element less than val, return undefined if no such element found.
*/
lower(value: T): T | undefined {
const compare = this.compareFn;
const dfs = (node: TreapNode<T> | null, value: T): T | undefined => {
if (node == null) return undefined;
if (compare(node.value, value) >= 0) return dfs(node.left, value);

const tmp = dfs(node.right, value);
if (tmp == null || compare(node.value, tmp) > 0) {
return node.value;
} else {
return tmp;
}
};

const res = dfs(this.root, value) as any;
return res === this.leftBound ? undefined : res;
}

/**
*
* @complexity O(logn)
* @description Find and return the element greater than val, return undefined if no such element found.
*/
higher(value: T): T | undefined {
const compare = this.compareFn;
const dfs = (node: TreapNode<T> | null, value: T): T | undefined => {
if (node == null) return undefined;
if (compare(node.value, value) <= 0) return dfs(node.right, value);

const tmp = dfs(node.left, value);

if (tmp == null || compare(node.value, tmp) < 0) {
return node.value;
} else {
return tmp;
}
};

const res = dfs(this.root, value) as any;
return res === this.rightBound ? undefined : res;
}

/**
*
* @complexity O(logn)
* @description Find and return the element less than or equal to val, return undefined if no such element found.
*/
floor(value: T): T | undefined {
const compare = this.compareFn;
const dfs = (node: TreapNode<T> | null, value: T): T | undefined => {
if (node == null) return undefined;
if (compare(node.value, value) === 0) return node.value;
if (compare(node.value, value) >= 0) return dfs(node.left, value);

const tmp = dfs(node.right, value);
if (tmp == null || compare(node.value, tmp) > 0) {
return node.value;
} else {
return tmp;
}
};

const res = dfs(this.root, value) as any;
return res === this.leftBound ? undefined : res;
}

/**
*
* @complexity O(logn)
* @description Find and return the element greater than or equal to val, return undefined if no such element found.
*/
ceil(value: T): T | undefined {
const compare = this.compareFn;
const dfs = (node: TreapNode<T> | null, value: T): T | undefined => {
if (node == null) return undefined;
if (compare(node.value, value) === 0) return node.value;
if (compare(node.value, value) <= 0) return dfs(node.right, value);

const tmp = dfs(node.left, value);

if (tmp == null || compare(node.value, tmp) < 0) {
return node.value;
} else {
return tmp;
}
};

const res = dfs(this.root, value) as any;
return res === this.rightBound ? undefined : res;
}

/**
* @complexity O(logn)
* @description
* Returns the last element from set.
* If the set is empty, undefined is returned.
*/
first(): T | undefined {
const iter = this.inOrder();
iter.next();
const res = iter.next().value;
return res === this.rightBound ? undefined : res;
}

/**
* @complexity O(logn)
* @description
* Returns the last element from set.
* If the set is empty, undefined is returned .
*/
last(): T | undefined {
const iter = this.reverseInOrder();
iter.next();
const res = iter.next().value;
return res === this.leftBound ? undefined : res;
}

/**
* @complexity O(logn)
* @description
* Removes the first element from an set and returns it.
* If the set is empty, undefined is returned and the set is not modified.
*/
shift(): T | undefined {
const first = this.first();
if (first === undefined) return undefined;
this.delete(first);
return first;
}

/**
* @complexity O(logn)
* @description
* Removes the last element from an set and returns it.
* If the set is empty, undefined is returned and the set is not modified.
*/
pop(index?: number): T | undefined {
if (index == null) {
const last = this.last();
if (last === undefined) return undefined;
this.delete(last);
return last;
}

const toDelete = this.at(index);
if (toDelete == null) return;
this.delete(toDelete);
}

/**
*
* @complexity O(logn)
* @description
* Returns number of occurrences of value in the sorted set.
*/
count(value: T): number {
const compare = this.compareFn;
const dfs = (node: TreapNode<T> | null, value: T): number => {
if (node == null) return 0;
if (compare(node.value, value) === 0) return node.count;
if (compare(node.value, value) < 0) return dfs(node.right, value);
return dfs(node.left, value);
};

return dfs(this.root, value);
}

*[Symbol.iterator](): Generator<T, any, any> {
yield* this.values();
}

/**
* @description
* Returns an iterable of keys in the set.
*/
*keys(): Generator<T, any, any> {
yield* this.values();
}

/**
* @description
* Returns an iterable of values in the set.
*/
*values(): Generator<T, any, any> {
const iter = this.inOrder();
iter.next();
const steps = this.size;
for (let _ = 0; _ < steps; _++) {
yield iter.next().value;
}
}

/**
* @description
* Returns a generator for reversed order traversing the set.
*/
*rvalues(): Generator<T, any, any> {
const iter = this.reverseInOrder();
iter.next();
const steps = this.size;
for (let _ = 0; _ < steps; _++) {
yield iter.next().value;
}
}

/**
* @description
* Returns an iterable of key, value pairs for every entry in the set.
*/
*entries(): IterableIterator<[number, T]> {
const iter = this.inOrder();
iter.next();
const steps = this.size;
for (let i = 0; i < steps; i++) {
yield [i, iter.next().value];
}
}

private *inOrder(root: TreapNode<T> | null = this.root): Generator<T, any, any> {
if (root == null) return;
yield* this.inOrder(root.left);
const count = root.count;
for (let _ = 0; _ < count; _++) {
yield root.value;
}
yield* this.inOrder(root.right);
}

private *reverseInOrder(root: TreapNode<T> | null = this.root): Generator<T, any, any> {
if (root == null) return;
yield* this.reverseInOrder(root.right);
const count = root.count;
for (let _ = 0; _ < count; _++) {
yield root.value;
}
yield* this.reverseInOrder(root.left);
}
}