日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

KD树(kd tree)

發布時間:2023/12/19 综合教程 35 生活家
生活随笔 收集整理的這篇文章主要介紹了 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)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。