Skip to main content

Misc

Convex hull is a fundamental geometric algorithm that is used to compute the smallest convex polygon that contains a set of points in the plane. The convex hull of a set of points is the smallest convex polygon that encloses all the points. The idea behind the convex hull algorithm is to first sort the points based on their x-coordinate (or y-coordinate, depending on the implementation), and then use a "stack" to keep track of the points that are part of the convex hull. Starting with the leftmost point, we add each point to the stack one by one, making sure to remove any points that would cause the polygon to become non-convex.

public class ConvexHull {
private static int crossProduct(Point p1, Point p2, Point p3) {
int x1 = p2.x - p1.x;
int y1 = p2.y - p1.y;
int x2 = p3.x - p1.x;
int y2 = p3.y - p1.y;
return x1 * y2 - x2 * y1;
}

public static List<Point> convexHull(List<Point> points) {
int n = points.size();
if (n < 3) return points;
points.sort((p1, p2) -> {
if (p1.x != p2.x) return p1.x - p2.x;
return p1.y - p2.y;
});
List<Point> hull = new ArrayList<>();
hull.add(points.get(0));
hull.add(points.get(1));
for (int i = 2; i < n; ++i) {
Point p = points.get(i);
while (hull.size() >= 2) {
Point top = hull.get(hull.size() - 1);
Point secondTop = hull.get(hull.size() - 2);
if (crossProduct(secondTop, top, p) <= 0) {
hull.remove(hull.size() - 1);
} else {
break;
}
}
hull.add(p);
}
return hull;
}
}

Note that the crossProduct method calculates the cross product of two vectors p1p2 and p1p3, where p1, p2, and p3 are points in 2D space. The sign of the cross product indicates whether p1p3 is to the left or to the right of p1p2. If we have p1p3 to the right of p1p2 we just continue adding the new point, but if we have it on the left that means we have to remove the last point we added since we have a better path now (connecting p1p3 instead p1p2). The time complexity of the algorithm is just that of sorting (O(n logn)).

A sweep line algorithm is a computational geometry technique that involves the use of a moving line, or sweep line, to solve geometric problems. This algorithm is often used to solve problems related to lines and polygons in two-dimensional space.

class Point implements Comparable<Point> {
double x, y;

Point(double x, double y) {
this.x = x;
this.y = y;
}

// Compare points by x-coordinate
public int compareTo(Point other) {
return Double.compare(this.x, other.x);
}
}

class Pair {
Point p1, p2;
double distance;

Pair(Point p1, Point p2, double distance) {
this.p1 = p1;
this.p2 = p2;
this.distance = distance;
}
}

public class ClosestPair {
public static Pair closestPair(Point[] points) {
Arrays.sort(points); // sort points by x-coordinate
TreeSet<Point> activeSet = new TreeSet<>((p1, p2) -> Double.compare(p1.y, p2.y));
int j = 0;
double bestDistance = Double.POSITIVE_INFINITY;
Pair bestPair = null;
for (int i = 0; i < points.length; i++) {
Point p1 = points[i];
while (j < i && points[j].x < p1.x - bestDistance) {
activeSet.remove(points[j]);
j++;
}
Point lo = new Point(p1.x - bestDistance, p1.y - bestDistance);
Point hi = new Point(p1.x + bestDistance, p1.y + bestDistance);
for (Point p2 : activeSet.subSet(lo, hi)) {
double distance = distance(p1, p2);
if (distance < bestDistance) {
bestDistance = distance;
bestPair = new Pair(p1, p2, distance);
}
}
activeSet.add(p1);
}
return bestPair;
}

private static double distance(Point p1, Point p2) {
double dx = p1.x - p2.x;
double dy = p1.y - p2.y;
return Math.sqrt(dx*dx + dy*dy);
}
}

In this implementation, we use a TreeSet to maintain an "active set" of points that are within the current best distance from the current point being considered. We iterate over the points in order of increasing x-coordinate, and for each point, we remove any points from the active set that are too far to the left to be within the current best distance. We then consider all points in the active set that are within the current best distance to the current point, and update the best pair and best distance if we find a pair of points that is closer than the current best pair. Finally, we add the current point to the active set.

Segment tree for range search/update:

We can represent a segment tree as an array of 2n elements, where n is the size of the original array and a power of two. The nodes of the tree are stored in a top-down order, with the top node being stored at tree[0], its children at tree[1] and tree[2], and so on. The values of the original array are stored in the bottom level of the tree, from tree[n] to tree[2n-1].

In this representation, the parent of a node at tree[k] is located at tree[floor(k/2)], and its left and right children are located at tree[2k] and tree[2k+1], respectively. It should be noted that a node is located at an even position if it is a left child and at an odd position if it is a right child.

public static int sum(int a, int b) {
a += n; b += n;
int s = 0;
while (a <= b) {
if (a%2 == 1) s += tree[a++];
if (b%2 == 0) s += tree[b--];
a /= 2; b /= 2;
}
return s;
}

void add(int k, int x) {
k += n;
tree[k] += x;
for (k /= 2; k >= 1; k /= 2) {
tree[k] = tree[2*k]+tree[2*k+1];
}
}

// can support both search and update with lazy propagation
public static int sum(int a, int b, int k, int x, int y) {
if (b < x || a > y) return 0;
if (a <= x && y <= b) return tree[k];
int d = (x+y)/2;
return sum(a,b,2*k,x,d) + sum(a,b,2*k+1,d+1,y);
}
int s = sum(a, b, 1, 0, n-1);