刚开始学习,介绍先搁着~等理解透彻了再来写~~~
我是学习的mzry1992(UESTC_Izayoi ~)------http://www.mzry1992.com/blog/miao/kd%E6%A0%91.html
先去看mzry1992大牛博客里的讲解吧。。。
再附两篇论文:(看英文看得好爽。。。~@.@)
《An intoductory tutorial on kd-trees》 ★(里面就介绍了kd-tree和nearest neighbour algorithm(最近邻算法)、Q nearest neighbour(Q近邻))
《Range Searching Using Kd-Tree.》
模板:
/* HDOJ 2966 KD-Tree模板 */ #include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <vector> #include <stack> #include <queue> #include <map> #include <algorithm> #include <string> #include <cstring> #define MID(x, y) ( (x + y)>>1 ) using namespace std; typedef long long LL; //KD-Tree模板 const int N=100005; LL res; struct Point { int x, y; //点是二维的,此时是2D-Tree }; LL dist2(const Point &a, const Point &b) //距离的平方 { return LL(a.x - b.x) * LL(a.x - b.x) + LL(a.y - b.y) * LL(a.y - b.y); } bool cmpX(const Point &a, const Point &b) { return a.x < b.x; } bool cmpY(const Point &a, const Point &b) { return a.y < b.y; } struct KDTree //很崇拜这种销魂的建树方法啊~0.0~很抽象很强大------p数组已经代表了KD-Tree了,神马左右子树全省了,OOOOOrrz!!!…… { Point p[N]; //空间内的点 int Div[N]; //记录区间是按什么方式划分(分割线平行于x轴还是y轴, ==1平行y轴切;==0平行x轴切) void build(int l, int r) //记得先把p备份一下。 { if (l > r) return; int mid=MID(l, r); 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); build(l, mid - 1); build(mid+1, r); } void find(int l, int r, Point a) //查找最近点的平方距离 { if (l > r) return; int mid = MID(l, r); LL dist = dist2(a, p[mid]); if (dist > 0) //如果有重点不能这么判断 res = min(res, dist); LL 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); if (d * d < res) find(l2, r2, a); } };
kd-tree入门题:
HDOJ 2966 In case of failure (最近邻,直接套模板~)
查找平面点最近点的距离(此题中是距离的平方)
1 /* 2 HDOJ 2966 3 KD-Tree模板 4 */ 5 #include <iostream> 6 #include <cstdio> 7 #include <cstdlib> 8 #include <cmath> 9 #include <vector> 10 #include <stack> 11 #include <queue> 12 #include <map> 13 #include <algorithm> 14 #include <string> 15 #include <cstring> 16 #define MID(x, y) ( (x + y)>>1 ) 17 18 using namespace std; 19 typedef long long LL; 20 21 //KD-Tree模板 22 const int N=100005; 23 LL res; 24 struct Point 25 { 26 int x, y; //点是二维的,此时是2D-Tree 27 }; 28 29 LL dist2(const Point &a, const Point &b) //距离的平方 30 { 31 return LL(a.x - b.x) * LL(a.x - b.x) + LL(a.y - b.y) * LL(a.y - b.y); 32 } 33 34 bool cmpX(const Point &a, const Point &b) 35 { 36 return a.x < b.x; 37 } 38 bool cmpY(const Point &a, const Point &b) 39 { 40 return a.y < b.y; 41 } 42 43 struct KDTree //很崇拜这种销魂的建树方法啊~0.0~很抽象很强大------p数组已经代表了KD-Tree了,神马左右子树全省了,OOOOOrrz!!!…… 44 { 45 Point p[N]; //空间内的点 46 int Div[N]; //记录区间是按什么方式划分(分割线平行于x轴还是y轴, ==1平行y轴切;==0平行x轴切) 47 48 void build(int l, int r) //记得先把p备份一下。 49 { 50 if (l > r) return; 51 int mid=MID(l, r); 52 int minX, minY, maxX, maxY; 53 minX = min_element(p + l, p + r + 1, cmpX)->x; 54 minY = min_element(p + l, p + r + 1, cmpY)->y; 55 maxX = max_element(p + l, p + r + 1, cmpX)->x; 56 maxY = max_element(p + l, p + r + 1, cmpY)->y; 57 Div[mid] = (maxX - minX >= maxY - minY); 58 nth_element(p + l, p + mid, p + r + 1, Div[mid] ? cmpX : cmpY); 59 build(l, mid - 1); 60 build(mid+1, r); 61 } 62 63 void find(int l, int r, Point a) //查找最近点的平方距离 64 { 65 if (l > r) return; 66 int mid = MID(l, r); 67 LL dist = dist2(a, p[mid]); 68 if (dist > 0) //如果有重点不能这么判断 69 res = min(res, dist); 70 LL d = Div[mid] ? (a.x - p[mid].x) : (a.y - p[mid].y); 71 int l1, l2, r1, r2; 72 l1 = l , l2 = mid + 1; 73 r1 = mid - 1, r2 = r; 74 if (d > 0) 75 swap(l1, l2), swap(r1, r2); 76 find(l1, r1, a); 77 if (d * d < res) 78 find(l2, r2, a); 79 } 80 }; 81 82 Point pp[N]; 83 KDTree kd; 84 85 int main() 86 { 87 //freopen("test.txt","r+",stdin); 88 int t; 89 scanf("%d", &t); 90 while(t--) 91 { 92 int n; 93 scanf("%d", &n); 94 for (int i = 0; i < n; i++) 95 { 96 scanf("%d%d", &pp[i].x, &pp[i].y); 97 kd.p[i] = pp[i]; 98 } 99 100 kd.build(0, n-1); 101 for (int i = 0; i < n; i++) 102 { 103 res = 9223372036854775807LL; 104 kd.find(0, n - 1, pp[i]); 105 printf("%I64d\n", res); 106 } 107 } 108 return 0; 109 }
HDOJ 4347 The Closest M Points (Q近邻)
与上题不同的是,一是k维(这个好处理~),二是求最近的m个点而不单是最近点了。这个也好处理~递归查找时处理的时候采取如下策略:如果当前找到的点小于k个,那么两个区间都要处理。。否则根据当前找到的第k个点决定是否去另外一个区间,如果目标点到分界线的距离大于等于已经找到的第k远的点,那么就不用查找另一个分界了。。。更新答案可以用一个大小为k的堆去维护(一个最大堆,一旦超过k个点就把最大的扔掉)。。。
2s,速度还是不错的^_^~
6811512 | 2012-09-21 16:58:20 | Accepted | 4347 | 2421MS | 3848K | 4152 B | G++ | AbandonZHANG |
1 /* 2 HDOJ 2966 3 KD-Tree模板 4 */ 5 #include <iostream> 6 #include <cstdio> 7 #include <cstdlib> 8 #include <cmath> 9 #include <vector> 10 #include <stack> 11 #include <queue> 12 #include <set> 13 #include <map> 14 #include <algorithm> 15 #include <string> 16 #include <cstring> 17 #define MID(x,y) ( (x + y)>>1 ) 18 19 using namespace std; 20 typedef long long LL; 21 22 //KD-Tree模板 23 const int N=50005; 24 25 struct Point 26 { 27 int x[5]; 28 LL dis; 29 Point() 30 { 31 for (int i = 0; i < 5; i++) 32 x[i] = 0; 33 dis = 9223372036854775807LL; 34 } 35 friend bool operator < (const Point &a, const Point &b) 36 { 37 return a.dis < b.dis; 38 } 39 }; 40 priority_queue <Point, vector<Point> > res; 41 42 LL dist2(const Point &a, const Point &b, int k) //距离的平方,开根号很耗时,能不开就不开 43 { 44 LL ans = 0; 45 for (int i = 0; i < k; i++) //一开始这儿写的i < 5,WA了N次。。。原本以为只要初始值设0就无所谓, 46 ans += (a.x[i] - b.x[i]) * (a.x[i] - b.x[i]); //但发现Point有个全局变量,在多case下会出错。。。 47 return ans; 48 } 49 50 int ddiv; 51 bool cmpX(const Point &a, const Point &b) 52 { 53 return a.x[ddiv] < b.x[ddiv]; 54 } 55 56 struct KDTree //很崇拜这种销魂的建树方法啊~0.0~很抽象很强大------p数组已经代表了KD-Tree了,神马左右子树全省了,OOOOOrrz!!!…… 57 { 58 Point p[N]; //空间内的点 59 int Div[N]; //记录区间是按什么方式划分(分割线平行于x轴还是y轴, ==1平行y轴切;==0平行x轴切) 60 int k; //维数 61 int m; //近邻 62 63 int getDiv(int l, int r) //寻找区间跨度最大的划分方式 64 { 65 map <int, int> ms; 66 int minx[5],maxx[5]; 67 for (int i = 0; i < k; i++) 68 { 69 ddiv = i; 70 minx[i] = min_element(p + l, p + r + 1, cmpX)->x[i]; 71 maxx[i] = max_element(p + l, p + r + 1, cmpX)->x[i]; 72 ms[maxx[i] - minx[i]] = i; 73 } 74 map <int ,int>::iterator pm = ms.end(); 75 pm--; 76 return pm->second; 77 } 78 79 void build(int l, int r) //记得先把p备份一下。 80 { 81 if (l > r) return; 82 int mid = MID(l,r); 83 Div[mid] = getDiv(l,r); 84 ddiv = Div[mid]; 85 nth_element(p + l, p + mid, p + r + 1, cmpX); 86 build(l, mid - 1); 87 build(mid + 1, r); 88 } 89 90 void findk(int l, int r, Point a) //k(m)近邻,查找k近点的平方距离 91 { 92 if (l > r) return; 93 int mid = MID(l,r); 94 LL dist = dist2(a, p[mid], k); 95 if (dist >= 0) 96 { 97 p[mid].dis = dist; 98 res.push(p[mid]); 99 while ((int)res.size() > m) 100 res.pop(); 101 } 102 LL d = a.x[Div[mid]] - p[mid].x[Div[mid]]; 103 int l1, l2, r1, r2; 104 l1 = l , l2 = mid + 1; 105 r1 = mid - 1, r2 = r; 106 if (d > 0) 107 swap(l1, l2), swap(r1, r2); 108 findk(l1, r1, a); 109 if ((int)res.size() < m || d*d < res.top().dis ) 110 findk(l2, r2, a); 111 } 112 }; 113 114 Point pp[N]; 115 KDTree kd; 116 117 int main() 118 { 119 // freopen("test.txt","r+",stdin); 120 // freopen("ans.txt","w+",stdout); 121 122 int n; 123 while(scanf("%d%d", &n, &kd.k)!=EOF) 124 { 125 for (int i = 0; i < n; i++) 126 for (int j = 0; j < kd.k; j++) 127 { 128 scanf("%d", &pp[i].x[j]); 129 kd.p[i] = pp[i]; 130 } 131 kd.build(0, n - 1); 132 int t; 133 scanf("%d", &t); 134 while(t--) 135 { 136 Point a; 137 for (int i = 0; i < kd.k; i++) 138 scanf("%d", &a.x[i]); 139 scanf("%d", &kd.m); 140 kd.findk(0, n - 1, a); 141 printf("the closest %d points are:\n", kd.m); 142 Point ans[11]; 143 for (int i = 0; !res.empty(); i++) 144 { 145 ans[i] = res.top(); 146 res.pop(); 147 } 148 for (int i = kd.m - 1; i >= 0; i--) 149 { 150 for (int j = 0; j < kd.k - 1; j++) 151 printf("%d ", ans[i].x[j]); 152 printf("%d\n", ans[i].x[kd.k - 1]); 153 } 154 } 155 156 } 157 return 0; 158 }
(未完待续。。。)