K-D樹
#include <bits/stdc++.h> using namespace std; #define INT_INF 0x3fffffff #define EPS 1e-12 #define MOD 1000000007 #define PI 3.141592653579798 #define N 60000 struct data{ long long pos[10]; int id; } T[N] , op , point; int split[N],demension,now,n; bool use[N]; long long id,ans; double var[10]; bool cmp(data a,data b){ return a.pos[split[now]]<b.pos[split[now]]; } void build(int L,int R){ if(L>R) return; int mid=(L+R)>>1; for(int pos=0;pos<demension;pos++){ double ave=var[pos]=0.0; for(int i=L;i<=R;i++) ave+=T[i].pos[pos]; ave/=(R-L+1); for(int i=L;i<=R;i++) var[pos]+=(T[i].pos[pos]-ave)*(T[i].pos[pos]-ave); var[pos]/=(R-L+1); } split[now=mid]=0; for(int i=1;i<demension;i++) if(var[split[mid]]<var[i]) split[mid]=i; nth_element(T+L,T+mid,T+R+1,cmp); build(L,mid-1); build(mid+1,R); } void query(int L,int R){ if(L>R) return; int mid=(L+R)>>1; long long dis=0; for(int i=0;i<demension;i++) dis+=(op.pos[i]-T[mid].pos[i])*(op.pos[i]-T[mid].pos[i]); if(!use[T[mid].id] && dis<ans){ |
K-D樹 in 2 2 (2個二維點) out 0 0 the closest 1 1 1 points are: 1 (1個詢問) 1 1 3 3 (詢問點) 1 (求最近1個點) //pos[10]維度,id編號 //T[N] 樹中的結點 //op當前要查詢的結點(座標) //point取得最近距離下的點 //分裂方式,維度,半點數,總點數 //已使用陣列 //最近結點值及到分裂點距 //各維度方差儲存陣列
//區間為空則跳出 //取區間中點 //求出 每一維 上面的方差 //初始化VAR與平均值為0
//求此維度和 //求此維度平均值
//求此維度方差未平均 //求此維度方差
//半點數NOW用於後面排序,的確是從中間的點進憲分裂 //找到方差最大的那一維,用它來作為當前區間的 split_method //排第MID元素排到第MID的位置 //對左兒子建樹 //對右兒子建樹
//區間為空退出查詢 //求中點編號
//求出目標點 op 到現在的根節點(分裂點)的距離 //根結點可用且ans可更新 |
ans=dis; point=T[mid]; id=T[mid].id; } long long radius= (op.pos[split[mid]]-T[mid].pos[split[mid]])* (op.pos[split[mid]]-T[mid].pos[split[mid]]); if(op.pos[split[mid]]<T[mid].pos[split[mid]]){ query(L,mid-1); if(radius<=ans) query(mid+1,R); }else{ query(mid+1,R); if(radius<=ans) query(L,mid-1); } } int main(){ while(scanf("%d%d",&n,&demension)!=EOF){ for(int i=1;i<=n;i++){ for(int j=0;j<demension;j++) scanf("%I64d",&T[i].pos[j]); T[i].id=i; } build(1,n); int m,q; scanf("%d",&q); while(q--){ memset(use,0,sizeof(use)); for(int i=0;i<demension;i++) scanf("%I64d",&op.pos[i]); scanf("%d",&m); printf("the closest %d points are:\n",m); while(m--){ ans=(((long long)INT_INF)*INT_INF); query(1,n); for(int i=0;i<demension;i++){ printf("%I64d",point.pos[i]); if(i==demension-1) printf("\n"); else printf(" "); } use[id]=1; } } } return 0; } |
//更新最近距離 //更新取得最近距離下的點 //更新取得最近距離的點的 id
//計算 op 到分裂平面的距離的平方(當前要查詢的點到當前點集的中間點之差的平方) //對子區間進行查詢,小於則在左子樹,大於則在右子樹查,但是若到平面距小於等於到分裂點距則也要考慮另一邊
//確定要輸出多少個點及維度
//讀入 n 個點 //記錄本結點的編號,二叉樹的結點就反映了父親與兒子的關係 //建樹 // q 個詢問
//輸入所要查詢的點
//求到所給點最近的M個點 //(查一個刪一個) //初始化無限大 //詢問一次
//把詢問所得到的點逐維度輸出 //簡單的換行與空格
//此結點已使用 |