1. 程式人生 > >K-D樹演算法的一些總結

K-D樹演算法的一些總結

在2016年ACM ICPC青島站的比賽中,一道K-D樹問題成為了金銀牌的分界線,最後由我們隊一個強力隊友寫出了該題,其實那題本來該由我負責的,都怪我學藝不精。

k-d樹(k-dimensional樹的簡稱),是一種分割k維資料空間的資料結構。主要應用於多維空間關鍵資料的搜尋(如:範圍搜尋和最近鄰搜尋)。

應用背景

  SIFT演算法中做特徵點匹配的時候就會利用到k-d樹。而特徵點匹配實際上就是一個通過距離函式在高維向量之間進行相似性檢索的問題。針對如何快速而準確地找到查詢點的近鄰,現在提出了很多高維空間索引結構和近似查詢的演算法,k-d樹就是其中一種。

  索引結構中相似性查詢有兩種基本的方式:一種是範圍查詢(range searches),另一種是K近鄰查詢(K-neighbor searches)。範圍查詢就是給定查詢點和查詢距離的閾值,從資料集中找出所有與查詢點距離小於閾值的資料;K近鄰查詢是給定查詢點及正整數K,從資料集中找到距離查詢點最近的K個數據,當K=1時,就是最近鄰查詢(nearest neighbor searches)。

  特徵匹配運算元大致可以分為兩類。一類是線性掃描法,即將資料集中的點與查詢點逐一進行距離比較,也就是窮舉,缺點很明顯,就是沒有利用資料集本身蘊含的任何結構資訊,搜尋效率較低,第二類是建立資料索引,然後再進行快速匹配。因為實際資料一般都會呈現出簇狀的聚類形態,通過設計有效的索引結構可以大大加快檢索的速度。索引樹屬於第二類,其基本思想就是對搜尋空間進行層次劃分。根據劃分的空間是否有混疊可以分為Clipping和Overlapping兩種。前者劃分空間沒有重疊,其代表就是k-d樹;後者劃分空間相互有交疊,其代表為R樹。(這裡只介紹k-d樹)

例項

  先以一個簡單直觀的例項來介紹k-d樹演算法。假設有6個二維資料點{(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)},資料點位於二維空間內(如圖1中黑點所示)。k-d樹演算法就是要確定圖1中這些分割空間的分割線(多維空間即為分割平面,一般為超平面)。下面就要通過一步步展示k-d樹是如何確定這些分割線的。

圖1  二維資料k-d樹空間劃分示意圖

  k-d樹演算法可以分為兩大部分,一部分是有關k-d樹本身這種資料結構建立的演算法,另一部分是在建立的k-d樹上如何進行最鄰近查詢的演算法。

再附上HDU2966我的程式碼:

題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=2966

#include <map>
#include <set>
#include <queue>
#include <cmath>
#include <stack>
#include <cstdio>
#include <cctype>
#include <vector>
#include <string>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std; typedef long long LL; const int N = 100050; struct Point { LL x[3]; }p[N], ori[N]; int split[20], cur, dim; bool cmp(const Point &a, const Point &b) { return a.x[cur] < b.x[cur]; } #define lson l, m - 1, depth + 1 #define rson m + 1, r, depth + 1 template <class T> T sqr(T x) { return x * x; } const LL inf = 0x7777777777777777ll; LL dist(const Point &x, const Point &y) { LL ret = 0; for (int i = 0; i < dim; i++) { ret += sqr(x.x[i] - y.x[i]); } return ret ? ret : inf; } void build(const int &l, const int &r, const int &depth) { if (l >= r) return; int m = l + r >> 1; cur = depth % dim; nth_element(p + l, p + m, p + r + 1, cmp); build(lson); build(rson); } LL Find(const Point &x, const int &l, const int &r, const int &depth) { int cur = depth % dim; if (l >= r) { if (l == r) return dist(x, p[l]); return inf; } int m = l + r >> 1; LL ret = dist(x, p[m]), tmp; if (x.x[cur] < p[m].x[cur]) { tmp = Find(x, lson); if (tmp > sqr(x.x[cur] - p[m].x[cur])) { tmp = min(tmp, Find(x, rson)); } } else { tmp = Find(x, rson); if (tmp > sqr(x.x[cur] - p[m].x[cur])) { tmp = min(tmp, Find(x, lson)); } } return min(ret, tmp); } int main() { int n, T; scanf("%d", &T); while (T--) { scanf("%d", &n); dim = 2; for (int i = 0; i < n; i++) { for (int j = 0; j < 2; j++) { scanf("%I64d", &ori[i].x[j]); } p[i] = ori[i]; } build(0, n - 1, 0); for (int i = 0; i < n; i++) { printf("%I64d\n", Find(ori[i], 0, n - 1, 0)); } } return 0; }