Algoritmos fundamentales: convex hull, closest pair, Voronoi, sweep line y más.
Giro a la izquierda (CCW)
Giro a la derecha (CW)
Puntos colineales
double cross(Point O, Point A, Point B) {
return (A.x - O.x) * (B.y - O.y) - (A.y - O.y) * (B.x - O.x);
}
// Orientación: >0 CCW, <0 CW, =0 colineal
int orient(Point a, Point b, Point c) {
double v = cross(a, b, c);
if (v > 0) return 1; // CCW
if (v < 0) return -1; // CW
return 0; // colineal
}
// Test si segmentos AB y CD se intersectan
bool segmentsIntersect(Point A, Point B, Point C, Point D) {
int o1 = orient(A, B, C), o2 = orient(A, B, D);
int o3 = orient(C, D, A), o4 = orient(C, D, B);
if (o1 * o2 < 0 && o3 * o4 < 0) return true;
// Casos colineales...
return false;
}
def cross(o, a, b):
return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0])
def orient(a, b, c):
v = cross(a, b, c)
if v > 0: return 1 # CCW
if v < 0: return -1 # CW
return 0 # colineal
def segments_intersect(a, b, c, d):
o1, o2 = orient(a, b, c), orient(a, b, d)
o3, o4 = orient(c, d, a), orient(c, d, b)
if o1 * o2 < 0 and o3 * o4 < 0:
return True
# Casos colineales...
return False
double closestPair(vector<Point>& Px, vector<Point>& Py) {
int n = Px.size();
if (n <= 3) return bruteForce(Px);
int mid = n / 2;
Point midPoint = Px[mid];
vector<Point> Lx(Px.begin(), Px.begin() + mid);
vector<Point> Rx(Px.begin() + mid, Px.end());
vector<Point> Ly, Ry;
for (auto& p : Py) {
if (p.x <= midPoint.x) Ly.push_back(p);
else Ry.push_back(p);
}
double dL = closestPair(Lx, Ly);
double dR = closestPair(Rx, Ry);
double d = min(dL, dR);
// Franja
vector<Point> strip;
for (auto& p : Py)
if (abs(p.x - midPoint.x) < d) strip.push_back(p);
for (int i = 0; i < strip.size(); i++)
for (int j = i + 1; j < strip.size() && strip[j].y - strip[i].y < d; j++)
d = min(d, dist(strip[i], strip[j]));
return d;
}
| Evento | Acción |
|---|---|
| Left endpoint | Insertar segmento en status; check con vecinos |
| Right endpoint | Eliminar segmento; check nuevos vecinos entre sí |
| Intersection | Swap segmentos en status; check nuevos vecinos |
| Algoritmo | Tiempo | Descripción |
|---|---|---|
| Graham Scan | O(n log n) | Ordenar por ángulo polar, mantener stack |
| Jarvis March | O(nh) | Gift wrapping, output-sensitive |
| Chan's Algorithm | O(n log h) | Combina Graham + Jarvis |
| QuickHull | O(n log n) avg | Divide & conquer, worst O(n²) |
vector<Point> grahamScan(vector<Point> pts) {
int n = pts.size();
if (n < 3) return pts;
// Encontrar punto más bajo
int pivot = 0;
for (int i = 1; i < n; i++)
if (pts[i].y < pts[pivot].y ||
(pts[i].y == pts[pivot].y && pts[i].x < pts[pivot].x))
pivot = i;
swap(pts[0], pts[pivot]);
Point p0 = pts[0];
// Ordenar por ángulo polar
sort(pts.begin() + 1, pts.end(), [&](Point a, Point b) {
double v = cross(p0, a, b);
if (v == 0) return dist(p0, a) < dist(p0, b);
return v > 0;
});
// Construir hull
vector<Point> hull;
for (auto& p : pts) {
while (hull.size() >= 2 &&
cross(hull[hull.size()-2], hull[hull.size()-1], p) <= 0)
hull.pop_back();
hull.push_back(p);
}
return hull;
}
def graham_scan(pts):
if len(pts) < 3:
return pts
# Encontrar punto más bajo
p0 = min(pts, key=lambda p: (p[1], p[0]))
pts = sorted(pts, key=lambda p: (
math.atan2(p[1] - p0[1], p[0] - p0[0]),
(p[0] - p0[0])**2 + (p[1] - p0[1])**2
))
# Construir hull
hull = []
for p in pts:
while len(hull) >= 2 and cross(hull[-2], hull[-1], p) <= 0:
hull.pop()
hull.append(p)
return hull
Divide el plano en regiones por cercanía
Fortune's sweep: O(n log n)
Maximiza el ángulo mínimo
Ningún punto dentro del circumcírculo
Preprocessing: O(n), Space: O(n), Query: O(log n)
| Estructura | Preproceso | Query | Espacio |
|---|---|---|---|
| Range Tree | O(n log^(d-1) n) | O(log^d n + k) | O(n log^(d-1) n) |
| kd-Tree | O(n log n) | O(n^(1-1/d) + k) | O(n) |
| Fractional Cascading | O(n log n) | O(log^(d-1) n + k) | O(n log^(d-1) n) |
struct KDNode {
Point pt;
KDNode *left, *right;
};
KDNode* build(vector<Point>& pts, int depth = 0) {
if (pts.empty()) return nullptr;
int axis = depth % 2;
sort(pts.begin(), pts.end(), [axis](auto& a, auto& b) {
return axis == 0 ? a.x < b.x : a.y < b.y;
});
int mid = pts.size() / 2;
KDNode* node = new KDNode{pts[mid], nullptr, nullptr};
vector<Point> left(pts.begin(), pts.begin() + mid);
vector<Point> right(pts.begin() + mid + 1, pts.end());
node->left = build(left, depth + 1);
node->right = build(right, depth + 1);
return node;
}
Tiempo esperado: O(n)
Cada constraint causa violación con probabilidad ≤ 2/i
1. ¿Cuál es la complejidad del algoritmo Closest Pair con divide & conquer?
2. Si cross(O, A, B) > 0, ¿qué indica sobre el giro O→A→B?
3. ¿Cuál algoritmo de convex hull es output-sensitive?
4. La triangulación de Delaunay es el grafo dual de:
5. ¿Cuál es la complejidad esperada de Seidel's 2D LP?
6. Bentley-Ottmann reporta k intersecciones de n segmentos en: