1. 程式人生 > >K-D樹

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個點

//(查一個刪一個)

//初始化無限大

//詢問一次

 

//把詢問所得到的點逐維度輸出

//簡單的換行與空格

 

 

//此結點已使用