点点关系

点点关系

问题描述

点点关系处理平面上两点之间的基本关系,包括距离计算、向量运算等。常用于最近点对、凸包等问题。

基本操作

算法思想

  1. 使用坐标表示点的位置
  2. 基于向量进行计算
  3. 适用于距离和方向判断
  4. 时间复杂度

代码实现

class Point {
public:
    double x, y;
    
    Point(double x = 0, double y = 0) : x(x), y(y) {}
    
    // 向量加法
    Point operator + (const Point& p) const {
        return Point(x + p.x, y + p.y);
    }
    
    // 向量减法
    Point operator - (const Point& p) const {
        return Point(x - p.x, y - p.y);
    }
    
    // 点积
    double dot(const Point& p) const {
        return x * p.x + y * p.y;
    }
    
    // 叉积
    double cross(const Point& p) const {
        return x * p.y - y * p.x;
    }
    
    // 欧氏距离
    double distance(const Point& p) const {
        return sqrt((x - p.x) * (x - p.x) + (y - p.y) * (y - p.y));
    }
    
    // 曼哈顿距离
    double manhattanDistance(const Point& p) const {
        return abs(x - p.x) + abs(y - p.y);
    }
};
class Point {
    double x, y;
    
    Point(double x, double y) {
        this.x = x;
        this.y = y;
    }
    
    // 向量加法
    Point add(Point p) {
        return new Point(x + p.x, y + p.y);
    }
    
    // 向量减法
    Point subtract(Point p) {
        return new Point(x - p.x, y - p.y);
    }
    
    // 点积
    double dot(Point p) {
        return x * p.x + y * p.y;
    }
    
    // 叉积
    double cross(Point p) {
        return x * p.y - y * p.x;
    }
    
    // 欧氏距离
    double distance(Point p) {
        return Math.sqrt((x - p.x) * (x - p.x) + (y - p.y) * (y - p.y));
    }
    
    // 曼哈顿距离
    double manhattanDistance(Point p) {
        return Math.abs(x - p.x) + Math.abs(y - p.y);
    }
}
class Point:
    def __init__(self, x: float = 0, y: float = 0):
        self.x = x
        self.y = y
    
    # 向量加法
    def __add__(self, p: 'Point') -> 'Point':
        return Point(self.x + p.x, self.y + p.y)
    
    # 向量减法
    def __sub__(self, p: 'Point') -> 'Point':
        return Point(self.x - p.x, self.y - p.y)
    
    # 点积
    def dot(self, p: 'Point') -> float:
        return self.x * p.x + self.y * p.y
    
    # 叉积
    def cross(self, p: 'Point') -> float:
        return self.x * p.y - self.y * p.x
    
    # 欧氏距离
    def distance(self, p: 'Point') -> float:
        return ((self.x - p.x) ** 2 + (self.y - p.y) ** 2) ** 0.5
    
    # 曼哈顿距离
    def manhattan_distance(self, p: 'Point') -> float:
        return abs(self.x - p.x) + abs(self.y - p.y)

时间复杂度分析

操作 时间复杂度 空间复杂度
向量运算
点积叉积
距离计算
方向判断

应用场景

  1. 距离计算
  2. 向量运算
  3. 方向判断
  4. 凸包构建
  5. 最近点对

注意事项

  1. 浮点数精度
  2. 除零判断
  3. 特殊情况
  4. 坐标范围
  5. 数值稳定性

常见变形

  1. 最近点对
class Solution {
public:
    // 分治法求最近点对
    double closestPair(vector<Point>& points) {
        sort(points.begin(), points.end(), 
             [](const Point& a, const Point& b) {
                 return a.x < b.x;
             });
        return closest(points, 0, points.size() - 1);
    }
    
private:
    double closest(vector<Point>& points, int left, int right) {
        if (right - left <= 3) {
            return bruteForce(points, left, right);
        }
        
        int mid = (left + right) / 2;
        double midX = points[mid].x;
        double d1 = closest(points, left, mid);
        double d2 = closest(points, mid + 1, right);
        double d = min(d1, d2);
        
        vector<Point> strip;
        for (int i = left; i <= right; i++) {
            if (abs(points[i].x - midX) < d) {
                strip.push_back(points[i]);
            }
        }
        
        return min(d, stripClosest(strip, d));
    }
    
    double stripClosest(vector<Point>& strip, double d) {
        sort(strip.begin(), strip.end(),
             [](const Point& a, const Point& b) {
                 return a.y < b.y;
             });
        
        double minDist = d;
        for (int i = 0; i < strip.size(); i++) {
            for (int j = i + 1; j < strip.size() && 
                 strip[j].y - strip[i].y < minDist; j++) {
                minDist = min(minDist, strip[i].distance(strip[j]));
            }
        }
        return minDist;
    }
    
    double bruteForce(vector<Point>& points, int left, int right) {
        double minDist = DBL_MAX;
        for (int i = left; i < right; i++) {
            for (int j = i + 1; j <= right; j++) {
                minDist = min(minDist, points[i].distance(points[j]));
            }
        }
        return minDist;
    }
};
class Solution {
    // 分治法求最近点对
    public double closestPair(Point[] points) {
        Arrays.sort(points, (a, b) -> Double.compare(a.x, b.x));
        return closest(points, 0, points.length - 1);
    }
    
    private double closest(Point[] points, int left, int right) {
        if (right - left <= 3) {
            return bruteForce(points, left, right);
        }
        
        int mid = (left + right) / 2;
        double midX = points[mid].x;
        double d1 = closest(points, left, mid);
        double d2 = closest(points, mid + 1, right);
        double d = Math.min(d1, d2);
        
        List<Point> strip = new ArrayList<>();
        for (int i = left; i <= right; i++) {
            if (Math.abs(points[i].x - midX) < d) {
                strip.add(points[i]);
            }
        }
        
        return Math.min(d, stripClosest(strip, d));
    }
    
    private double stripClosest(List<Point> strip, double d) {
        strip.sort((a, b) -> Double.compare(a.y, b.y));
        double minDist = d;
        
        for (int i = 0; i < strip.size(); i++) {
            for (int j = i + 1; j < strip.size() && 
                 strip.get(j).y - strip.get(i).y < minDist; j++) {
                minDist = Math.min(minDist, 
                    strip.get(i).distance(strip.get(j)));
            }
        }
        return minDist;
    }
    
    private double bruteForce(Point[] points, int left, int right) {
        double minDist = Double.MAX_VALUE;
        for (int i = left; i < right; i++) {
            for (int j = i + 1; j <= right; j++) {
                minDist = Math.min(minDist, points[i].distance(points[j]));
            }
        }
        return minDist;
    }
}
class Solution:
    # 分治法求最近点对
    def closest_pair(self, points: List[Point]) -> float:
        points.sort(key=lambda p: p.x)
        return self.closest(points, 0, len(points) - 1)
    
    def closest(self, points: List[Point], left: int, right: int) -> float:
        if right - left <= 3:
            return self.brute_force(points, left, right)
        
        mid = (left + right) // 2
        mid_x = points[mid].x
        d1 = self.closest(points, left, mid)
        d2 = self.closest(points, mid + 1, right)
        d = min(d1, d2)
        
        strip = []
        for i in range(left, right + 1):
            if abs(points[i].x - mid_x) < d:
                strip.append(points[i])
        
        return min(d, self.strip_closest(strip, d))
    
    def strip_closest(self, strip: List[Point], d: float) -> float:
        strip.sort(key=lambda p: p.y)
        min_dist = d
        
        for i in range(len(strip)):
            j = i + 1
            while j < len(strip) and strip[j].y - strip[i].y < min_dist:
                min_dist = min(min_dist, strip[i].distance(strip[j]))
                j += 1
        return min_dist
    
    def brute_force(self, points: List[Point], left: int, right: int) -> float:
        min_dist = float('inf')
        for i in range(left, right):
            for j in range(i + 1, right + 1):
                min_dist = min(min_dist, points[i].distance(points[j]))
        return min_dist
  1. 凸包构建
class Solution {
public:
    // Graham扫描法求凸包
    vector<Point> grahamScan(vector<Point>& points) {
        if (points.size() <= 3) return points;
        
        // 找到最下方的点
        int bottom = 0;
        for (int i = 1; i < points.size(); i++) {
            if (points[i].y < points[bottom].y ||
                (points[i].y == points[bottom].y && 
                 points[i].x < points[bottom].x)) {
                bottom = i;
            }
        }
        swap(points[0], points[bottom]);
        
        // 按极角排序
        Point p0 = points[0];
        sort(points.begin() + 1, points.end(),
             [&p0](const Point& a, const Point& b) {
                 double cross = (a - p0).cross(b - p0);
                 if (cross == 0) {
                     return p0.distance(a) < p0.distance(b);
                 }
                 return cross > 0;
             });
        
        // Graham扫描
        vector<Point> hull;
        hull.push_back(points[0]);
        hull.push_back(points[1]);
        
        for (int i = 2; i < points.size(); i++) {
            while (hull.size() >= 2 && 
                   (hull.back() - hull[hull.size() - 2])
                   .cross(points[i] - hull.back()) <= 0) {
                hull.pop_back();
            }
            hull.push_back(points[i]);
        }
        
        return hull;
    }
};

class Solution {
    // Graham扫描法求凸包
    public List<Point> grahamScan(Point[] points) {
        if (points.length <= 3) {
            return Arrays.asList(points);
        }
        
        // 找到最下方的点
        int bottom = 0;
        for (int i = 1; i < points.length; i++) {
            if (points[i].y < points[bottom].y ||
                (points[i].y == points[bottom].y && 
                 points[i].x < points[bottom].x)) {
                bottom = i;
            }
        }
        Point temp = points[0];
        points[0] = points[bottom];
        points[bottom] = temp;
        
        // 按极角排序
        final Point p0 = points[0];
        Arrays.sort(points, 1, points.length,
            (a, b) -> {
                double cross = a.subtract(p0).cross(b.subtract(p0));
                if (cross == 0) {
                    return Double.compare(p0.distance(a), p0.distance(b));
                }
                return cross > 0 ? -1 : 1;
            });
        
        // Graham扫描
        List<Point> hull = new ArrayList<>();
        hull.add(points[0]);
        hull.add(points[1]);
        
        for (int i = 2; i < points.length; i++) {
            while (hull.size() >= 2 && 
                   hull.get(hull.size()-1).subtract(
                       hull.get(hull.size()-2))
                   .cross(points[i].subtract(
                       hull.get(hull.size()-1))) <= 0) {
                hull.remove(hull.size()-1);
            }
            hull.add(points[i]);
        }
        
        return hull;
    }
}
class Solution:
    # Graham扫描法求凸包
    def graham_scan(self, points: List[Point]) -> List[Point]:
        if len(points) <= 3:
            return points
        
        # 找到最下方的点
        bottom = min(range(len(points)), 
                    key=lambda i: (points[i].y, points[i].x))
        points[0], points[bottom] = points[bottom], points[0]
        
        # 按极角排序
        p0 = points[0]
        points[1:] = sorted(
            points[1:],
            key=lambda p: (
                -(p - p0).cross(Point(1, 0)),
                p0.distance(p)
            )
        )
        
        # Graham扫描
        hull = [points[0], points[1]]
        
        for i in range(2, len(points)):
            while len(hull) >= 2 and \
                  (hull[-1] - hull[-2]).cross(points[i] - hull[-1]) <= 0:
                hull.pop()
            hull.append(points[i])
        
        return hull
牛客代码笔记-牛栋 文章被收录于专栏

汗牛充栋,学海无涯。<br/> 内含算法知识点讲解,以及牛客题库精选例题。<br/> 学习算法,从牛栋开始。

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务