KD树(kd tree)
維基百科介紹:http://en.wikipedia.org/wiki/Kdtree
KD樹是一種能在 O(N)時間內把平面劃分成若干個區域,然后在均攤 O(logN)的時間內找到某個區域內所有點的數據結構。
其思想是,每次把當前處理的區域按照點數分成兩部分,然后對兩部分進行遞歸處理。。。
分成兩部分有兩種策略:
一種是橫著豎著橫著豎著交替劃分。。
一種是把坐標跨度大的那一維劃分成兩部分。
似乎沒什么影響。
上圖是一種可行的劃分方式。每次找到當前處理點集中的中點,以這個中點為分界線把區間劃分成兩部分和。注意中點是作為分界線不參與下一輪處理。
查詢一個點的最近點時,首先令最近距離為,然后在KD樹中查找,首先和當前區間的中點求一次距離更新答案,然后再根據該點和中點的關系決定是去左區間還是右區間,如果正好在分界線上那么兩邊都過去吧。
這里還有個問題,可能點被分到了左區間,但是可能和右區間的某個點比較近。那么怎么辦。?
假設這個點到分界線的距離已經是大于等于當前最優答案了,那么另一個區間的所有點到這個點的距離都比當前最優答案遠,只有這時候不需要考慮另一個區間。
復雜度分析均攤下來的確是 O(N),詳情可以參觀一下《計算幾何:算法與應用》那本書。
STL中提供了一個叫做nth_element的函數,可以在O(n)的復雜度下找到序列的第k大數并且把序列以第k大為界分為兩半,用這個就能寫出很短的建樹過程了。
bool Div[MaxN];\\記錄這個區間是用什么體位劃分的
void BuildKD(int l, int r, Point p[])\\記得備份一下p
{
if (l > r) return;
int mid = l + r >> 1;
int minX, minY, maxX, maxY;
minX = min_element(p + l, p + r + 1, cmpX)->x;
minY = min_element(p + l, p + r + 1, cmpY)->y;
maxX = max_element(p + l, p + r + 1, cmpX)->x;
maxY = max_element(p + l, p + r + 1, cmpY)->y;
Div[mid] = (maxX - minX >= maxY - minY);
nth_element(p + l, p + mid, p + r + 1, Div[mid] ? cmpX : cmpY);
BuildKD(l, mid - 1, p);
BuildKD(mid + 1, r, p);
}
查找的時候照著思路寫就可以了。
void Find(int l, int r, Point a, Point p[])
{
if (l > r) return;
int mid = l + r >> 1;
long long dist = dist2(a, p[mid]);
if (dist > 0)//如果有重點不能這樣判斷
res = min(res, dist);
long long d = Div[mid] ? (a.x - p[mid].x) : (a.y - p[mid].y);
int l1, l2, r1, r2; l1 = l, l2 = mid + 1; r1 = mid - 1, r2 = r;
if (d > 0) swap(l1, l2), swap(r1, r2);
Find(l1, r1, a, p);
if (d * d < res) Find(l2, r2, a, p);
}
這份KD樹是參照佐倉杏子的代碼學習的。Orz。
相關題目:Incaseoffailure
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100000;
struct Point{
int x,y;
}p[N+10],tmp[N+10];
int Div[N+10];
long long ret;
int cmpx(const Point &a,const Point &b){
return a.x < b.x;
}
int cmpy(const Point &a,const Point &b){
return a.y < b.y;
}
void Build(int l,int r,Point Q[]){
if(l > r) return;
int m = (l+r)>>1;
int minx = min_element(Q+l,Q+r+1,cmpx)->x;
int miny = min_element(Q+l,Q+r+1,cmpy)->y;
int maxx = max_element(Q+l,Q+r+1,cmpx)->x;
int maxy = max_element(Q+l,Q+r+1,cmpy)->y;
Div[m] = (maxx-minx) >= (maxy-miny);
nth_element(Q+l,Q+m,Q+r+1,(Div[m]?cmpx:cmpy));
Build(l,m-1,Q);
Build(m+1,r,Q);
}
long long dis(Point a,Point b){
return 1LL*abs(a.x-b.x)*abs(a.x-b.x)+1LL*abs(a.y-b.y)*abs(a.y-b.y);
}
void find(int l,int r,Point q){
if(l > r) return;
int m = (l+r)>>1;
long long dist = dis(q,tmp[m]);
if( dist > 0 ) ret = min(ret,dist);
int d = Div[m] ? (q.x-tmp[m].x) : (q.y-tmp[m].y);
int l1,l2,r1,r2;
l1 = l, r1 = m-1;
l2 = m+1,r2 = r;
if(d > 0) swap(l1,l2),swap(r1,r2);
find(l1,r1,q);
if( 1LL*d*d < ret )
find(l2,r2,q);
}
int main(){
int t,n; scanf("%d",&t);
while(t-- && scanf("%d",&n)){
for(int i = 0; i < n; i++)
scanf("%d%d",&p[i].x,&p[i].y);
memcpy(tmp,p, sizeof(p));
Build(0,n-1,tmp);
for(int i = 0; i < n; i++){
ret = (1LL<<60);
find(0,n-1,p[i]);
printf("%I64d\n",ret);
}
}
return 0;
}
總結
以上是生活随笔為你收集整理的KD树(kd tree)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 取消对 null 指针“l”的引用。_C
- 下一篇: DSP 运行时间计算函数--_itoll