平面最近点对算法

1、问题

平面最近点对问题,给定一个平面内所有点的坐标,找出这些点中最近的两个点的距离。

2、解析

设平面上的点都在点集S中,为了将S线性分割为大小大致相等的2个子集S1和S2,我们选取一垂直线(x=m)来作为分割直线,其中m为S中各点x坐标的中位数。由此将S分割为S1={p∈S|px≤m}和S2={p∈S|px>m}。从而使S1和S2分别位于直线l的左侧和右侧,且S=S1∪S2 。由于m是S中各点x坐标值的中位数,因此S1和S2中的点数大致相等。 递归地在S1和S2上解最接近点对问题,我们分别得到S1和S2中的最小距离δ1和δ2。现设δ=min (δ1,δ2)。若S的最接近点对(p,q)之间的距离d(p,q)<δ则p和q必分属于S1和S2。不妨设p∈S1,q∈S2。那么p和q距直线l的距离均小于δ。因此,我们若用P1和P2分别表示直线l的左边和右边的宽为δ的2个垂直长条,则p∈S1,q∈S2.此时,P1中所有点与P2中所有点构成的点对均为最接近点对的候选者。可证明这种投影点最多只有6个。因此,若将P1和P2中所有S的点按其y坐标排好序,则对P1中所有点p,对排好序的点列作一次扫描,就可以找出所有最接近点对的候选者,对P1中每一点最多只要检查P2中排好序的相继6个点。

3、设计

 点归并

 1 void merge(int l, int r) {//归并排序l到r区间内的点 
 2     mid = l + r >> 1
 3     pos1 = l
 4     pos2 = mid + 1
 5     for i <- l to r 
 6         if p[pos1].y < p[pos2].y
 7             temp[i] = p[pos1++]
 8         else 
 9             temp[i] = p[pos2++]
10         if pos1 == mid + 1
11             break
12         if pos2 == r + 1
13             break
14     
15     for j <- i to r and pos1 <= mid
16         temp[j] = p[pos1++]
17     for j <- i to r and pos2 <= r
18         temp[j] = p[pos2++]
19     for k <- l to r
20         p[i] = temp[i]
21 }

分治思想分割点

 1 int GetMinDistance(int l, int r) {
 2     if l >= r
 3         return INT_MAX;//如果只有一个点返回无穷大 
 4     if l + 1 == r //如果只有两个点的话,l点在下,r点在上 
 5         if (p[l].y > p[r].y) 
 6             swap(p[l], p[r])
 7         return dis(l, r)
 8     }
 9     mid = l + r >> 1
10     PointMid_x = p[mid].x
11     cnt = 0
12     d = min(GetMinDistance(l, mid), GetMinDistance(mid + 1, r))//左右两个区间取最近距离 
13     merge(l, r)//归并区间 
14     for i <- l to r//找出里中心线距离为d之间的点 
15         if (p[i].x - PointMid_x) * (p[i].x - PointMid_x) < d 
16             interpoint[++cnt] = i
17     for i <- 1 to cnt//遍历找出的点,修改最小值d 
18         for j <- i + 1 to cnt
19             dist = (p[interpoint[j]].x - p[interpoint[i]].x) * (p[interpoint[j]].y - p[interpoint[i]].y)
20             if dist >= d
21             break
22             d = min(d, dis(interpoint[i], interpoint[j]));
23         }
24     }
25     return d//返回最小值d 
26 }

4、分析

平面点对算法需要多次分治和进行子问题合并操作
合并的复杂度为O(n),分治的子问题复杂度为 O(n/2)
总时间复杂度为O(n)+2*O(n/2)=O(nlogn)

5、源码

https://github.com/ChenyuWu0705/Algorithm-Analyze-and-Design/blob/main/PointMinDistance.cpp

posted @ 2021-04-18 22:25  programmer_w  阅读(304)  评论(0编辑  收藏  举报