zoukankan      html  css  js  c++  java
  • 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。
    相关题目:In case of failure

    #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;
    }
  • 相关阅读:
    406. 根据身高重建队列 vector 自定义排序
    5552. 到家的最少跳跃次数 BFS
    5602. 将 x 减到 0 的最小操作数 双指针
    Hadoop Container is running beyond memory limits code143
    1122. 数组的相对排序 自定义排序
    328. 奇偶链表 链表
    127. 单词接龙 bfs
    5600. 第 K 条最小指令 组合数学
    5548. 最小体力消耗路径 二分
    1024. 视频拼接 dp
  • 原文地址:https://www.cnblogs.com/yefeng1627/p/2842829.html
Copyright © 2011-2022 走看看