首先实现 Point 类,用于存储每一点的坐标数据。
class Point {
double x, y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
}
实现两点间距离计算方法 distance(p1, p2)
, 用于计算两点间距离。
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);
}
创建一个比较器,使得集合中的点按照它们的 x
或y
坐标从小到大排列。Collections.sort
是 Java 中用于对集合进行排序的方法,它接受一个比较器(Comparator
)作为参数,以确定如何排序集合中的元素。
// 按照它们的x坐标进行非递减排序
Collections.sort(points,
Comparator.comparingDouble(p -> p.x));
// 按照它们的y坐标进行非递减排序
Collections.sort(points,
Comparator.comparingDouble(p -> p.y));
当计算的点的数量小于等于3时,直接使用暴力算法求解最短距离。
double enumeratePair(List<Point> points, int low, int high) {
double minDistance = Double.POSITIVE_INFINITY;
for (int i = low; i <= high; i++) {
for (int j = i + 1; j <= high; j++) {
minDistance = Math.min(minDistance,
distance(points.get(i),
points.get(j)));
}
}
return minDistance;
}
实现merge(yl, yr)
方法,用于合并Yl和Yr,进行一次排序,避免在递归中调用排序算法。
List<Point> merge(List<Point> Yl, List<Point> Yr) {
List<Point> Y = new ArrayList<>(Yl.size() + Yr.size());
Y.addAll(Yl);
Y.addAll(Yr);
// 按照y坐标非递减排序
Collections.sort(Y,
Comparator.comparingDouble(p -> p.y));
return Y;
}
实现compute()
方法,用于计算T(临时列表)
中的点的最小距离。
double compute(List<Point> Y, double delta){
// 中间点的x坐标
double x0 = Y.get((int) (Y.size() / 2)).x;
// 临时列表
List<Point> T = new ArrayList<>();
int k = 0;
for (int i = 0; i < Y.size(); i++) {
Point point = Y.get(i);
if (Math.abs(point.x - x0) <= delta) {
k++;
T.add(point);
}
}
// 计算T的大小
int tSize = T.size();
double deltaPrime = 2 * delta;
// 对T中的点进行最小距离计算
for (int i = 0; i < tSize - 1; i++) {
for (int j = i + 1; j < Math.min(i + 8, tSize); j++) {
double dist = distance(T.get(i), T.get(j));
if (dist < delta) {
delta = dist;
}
}
}
// 返回最小距离
return delta;
}
实现cp()
方法,分治计算最短距离。
1 出口为当前子问题的规模小于3,可以直接求解最短距离。
if (high - low + 1 <= 3) {
return enumeratePair(points, low, high);
}
2 获取中点的索引,并使用cp(low, mid)
计算(δ1, Yl)
、cp(mid + 1, high)
计算(δr, Yr)
,获得δ = min{δl, δr}
。
int mid = (low + high) / 2;
Point midPoint = points.get(mid);
List<Point> Yl = new ArrayList<>();
List<Point> Yr = new ArrayList<>();
// 根据x坐标划分左右
for (Point y : ySorted) {
if (y.x <= points.get(mid).x) {
Yl.add(y);
} else {
Yr.add(y);
}
}
double deltaLeft = cp(points, yl, low, mid);
double deltaRight = cp(points, yr, mid + 1, high);
double delta = Math.min(deltaLeft, deltaRight);
3 归并Yl
与Yr
,计算临时列表中的最短距离,与两侧的最短距离进行比较,返回最小值。
List<Point> Y = merge(Yl, Yr);
return compute(Y, delta);
进行计算前,先对点集 points
进行预处理,按照x
坐标进行非递减排序。
int n = points.size();
if (n < 2) {
// 点数量无法构成一个点对,返回无穷大
return Double.POSITIVE_INFINITY;
}
// 按照它们的x坐标进行非递减排序
Collections.sort(points,
Comparator.comparingDouble(p -> p.x));
// 复制排序后的点集到Y列表
List<Point> ySorted = new ArrayList<>(points);
// 对Y列表按照y坐标进行非递减排序
Collections.sort(ySorted,
Comparator.comparingDouble(p -> p.y));
3.9 完整代码:
import java.util.*;
class Point {
double x, y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
}
public class ClosestPair {
// 计算两点之间的距离
public 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);
}
public static double enumeratePair(List<Point> points, int low, int high) {
double minDistance = Double.POSITIVE_INFINITY;
for (int i = low; i <= high; i++) {
for (int j = i + 1; j <= high; j++) {
minDistance = Math.min(minDistance, distance(points.get(i), points.get(j)));
}
}
return minDistance;
}
// 合并Yl和Yr
private static List<Point> merge(List<Point> Yl, List<Point> Yr) {
List<Point> Y = new ArrayList<>(Yl.size() + Yr.size());
Y.addAll(Yl);
Y.addAll(Yr);
// 按照y坐标非递减排序
Collections.sort(Y, Comparator.comparingDouble(p -> p.y));
return Y;
}
private static double compute(List<Point> Y, double delta){
// 中间点的x坐标
double x0 = Y.get((int) (Y.size() / 2) - 1).x;
// 临时列表
List<Point> T = new ArrayList<>();
int k = 0;
for (int i = 0; i < Y.size(); i++) {
Point point = Y.get(i);
if (Math.abs(point.x - x0) <= delta) {
k++;
T.add(point);
}
}
// 计算T的大小
int tSize = T.size();
double deltaPrime = 2 * delta;
// 对T中的点进行最小距离计算
for (int i = 0; i < tSize - 1; i++) {
for (int j = i + 1; j < Math.min(i + 8, tSize); j++) {
double dist = distance(T.get(i), T.get(j));
if (dist < delta) {
delta = dist;
}
}
}
// 返回最小距离
return delta;
}
// 递归计算最近点对的函数
private static double cp(List<Point> points, List<Point> ySorted, int low, int high) {
if (high - low + 1 <= 3) {
// 如果点的数量小于等于3,使用暴力方法计算最近点对距离
return enumeratePair(points, low, high);
}
int mid = (low + high) / 2;
List<Point> Yl = new ArrayList<>();
List<Point> Yr = new ArrayList<>();
for (Point y : ySorted) {
if (y.x <= points.get(mid).x) {
Yl.add(y);
} else {
Yr.add(y);
}
}
double deltaLeft = cp(points, Yl, low, mid);
double deltaRight = cp(points, Yr, mid + 1, high);
double delta = Math.min(deltaLeft, deltaRight);
List<Point> Y = merge(Yl, Yr);
return compute(Y, delta);
}
public static double closestPair(List<Point> points) {
if (points.size() < 2) {
// 如果点的数量小于2,无法计算最近点对距离
return Double.POSITIVE_INFINITY;
}
// 按照它们的x坐标进行非递减排序
Collections.sort(points,
Comparator.comparingDouble(p -> p.x));
// 复制排序后的点集到Y列表
List<Point> ySorted = new ArrayList<>(points);
// 对Y列表按照y坐标进行非递减排序
Collections.sort(ySorted,
Comparator.comparingDouble(p -> p.y));
return cp(points, ySorted, 0, points.size() - 1);
}
public static void main(String[] args) {
// 创建示例点集
List<Point> points = new ArrayList<>();
points.add(new Point(0, 0));
points.add(new Point(1, 2));
points.add(new Point(2, 4));
points.add(new Point(3, 1));
points.add(new Point(6, 7));
points.add(new Point(7, 0));
// 计算最近点对距离
double minDistance = closestPair(points);
System.out.println("最近点对距离为:" + minDistance);
}
}