当一个点集中只有两个点时,它们的距离就是最近距离;
当一个点集中有三个点时,它们的距离也可以直接求出;
当一个点集中的点数量大于3时,则将数组拆分成两半,分别计算左右子集的最近距离的平方d(将数量>3的数组一直拆分,最后到只有2个或3个点的点集时即能求解),收集距离中线两侧小于d的点,在这些点中寻找垂直距离小于d的两个点,若存在则说明其为当前能找到的最近点对,更新d为这两点的距离。
- #include<iostream>
- #include<algorithm>
- #include<cmath>
- using namespace std;
- /* 存储点的结构 */
- typedef struct{
- float x,y; // 点的x,y坐标
- }POINT;
- /* 辅助的点结构 */
- typedef struct{
- int index; // 点在X数组中的下标
- float x, y; // 点的x,y坐标
- }A_POINT;
- /* 对点进行递增顺序排序的比较 */
- bool compareX(POINT a, POINT b){
- return b.x>a.x;
- }
- /* 对辅助点进行递增排序的比较 */
- bool compareY(A_POINT a, A_POINT b){
- return b.y>a.y;
- }
- /* 计算两点距离的平方 */
- float dist(POINT a, POINT b){
- float dx, dy;
- dx = a.x - b.x, dy = a.y - b.y;
- return (dx*dx+dy*dy);
- }
- /************************************************************************
- * 求平面点集最近点对的分治算法
- *
- * 输入:存放平面点集点的数组X[]、辅助点数组Y[],数组起点下标low与终点下标high
- * 输出:最近点对a,b及距离d
- **********************************************************************/
- void closest(POINT X[], A_POINT Y[], int low, int high, POINT &a, POINT &b, float &d){
- int i,j,k,m;
- POINT al,bl,ar,br;
- float dl,dr;
- if((high-low)==1){ // 当n=2时直接计算
- a = X[low], b = X[high], d = dist(X[low], X[high]);
- }else{
- if((high-low)==2){ // 当n=3时直接计算
- dl = dist(X[low], X[low+1]);
- dr = dist(X[low], X[low+2]);
- d = dist(X[low+1], X[low+2]);
- if((dl<=dr)&&(dl<=d)){
- a = X[low], b = X[low+1], d = dl;
- }else{
- if(dr<=d){
- a = X[low], b = X[low+2], d= dr;
- }else{
- a = X[low+1], b = X[low+2];
- }
- }
- }else{ // 当n>3时进行分治
- A_POINT *SL = new A_POINT[(high-low)/2+1];
- A_POINT *SR = new A_POINT[(high-low)/2];
- m = (high-low)/2 + low; // 把x数组以m为界划分为两半
- j = k = 0;
- for(i=0; i<=high-low; i++){
- if(Y[i].index<=m){
- SL[j++] = Y[i]; // 收集左边子集中的最近点对
- }else{
- SR[k++] = Y[i]; // 收集右边子集中的最近点对
- }
- }
- closest(X,SL,low, m, al, bl, dl); // 计算左边子集的最近点对
- closest(X,SR,m+1, high, ar, br, dr);// 计算右边子集的最近点对
- if(dl<dr){ // 组合步得到左右子集中点的最短距离d
- a = al, b = bl, d = dl;
- }else{
- a = ar, b = br, d = dr;
- }
- POINT *Z = new POINT[high-low+1];
- k = 0;
- for( i=0; i<=high -low; i++){ // 收集距离中线距离小于d的元素,保存到数组Z(因Y数组按y坐标递增排序,Z数组也一样)
- if(fabs(X[m].x - Y[i].x)<d){
- Z[k].x = Y[i].x, Z[k++].y = Y[i].y;
- }
- }
- for( i=0; i<k; i++){
- for( j=i+1; (j<k)&&(Z[j].y-Z[i].y<d); j++){ // 若前后两点y轴的距离超过d则不可能使距离小于d,退出
- dl = dist(Z[i], Z[j]); // 计算前后两点的距离
- if(dl<d){ // 若小于d,则更新
- a = Z[i], b = Z[j], d = dl;
- }
- }
- }
- delete SL;
- delete SR;
- delete Z;
- }
- }
- }
- /**********************************************
- * 求平面点集最近点对的分治算法
- *
- * 输入:存放平面点集点的数组X[],点的个数n
- * 输出:最近点对a,b及距离d
- **********************************************/
- void closest_pair(POINT X[], int n, POINT &a, POINT &b, float &d){
- if(n<2){ // 当点集个数小于2时不存在最近点对
- d = 0;
- }else{
- sort(X,X+n, compareX); // 对x数组进行递增排序
- A_POINT *Y = new A_POINT[n]; // 初始化辅助点数组Y
- for( int i = 0 ; i < n ;i ++){
- Y[i].index = i;
- Y[i].x = X[i].x;
- Y[i].y = X[i].y;
- }
- sort(Y,Y+n, compareY); // 对y数组进行递增排序
- closest(X,Y,0,n-1,a,b,d); // 求亲密点对
- d = sqrt(d); // 将得到的d开平方才是两点间真正的距离
- delete Y;
- }
- }
- int main(){
- int n;
- cout<<"请输入点个数:";
- cin>>n;
- cout<<"请输入各个点的坐标:"<<endl;
- POINT *X = new POINT[n];
- for(int i=0; i<n; i++){
- cin>>X[i].x>>X[i].y;
- }
- POINT a,b;
- float d;
- closest_pair(X,n,a,b,d);
- if(n>=2){
- printf("(%.2f,%.2f) - (%.2f,%.2f) : %.2f/n", a.x, a.y, b.x, b.y, d);
- }else{
- printf("不存在最近点对!/n");
- }
- delete X;
- return 0;
- }