1. 程式人生 > >K-D Tree 學習筆記

K-D Tree 學習筆記

包含 root cto geo 平衡 二叉樹 啟發式搜索 queue typedef

K-D Tree 學習筆記

最近看了一下k-NN然後它說如果特征空間維數比較低的時候用K-D Tree來求k近鄰比較快所以就來補一下學OI時沒學的K-D Tree假裝寫一個學習筆記吧。

是什麽?

是一個平衡二叉樹

k=1的時候就是一只BST

k>1的話,每一層換一維來分割

就是用許多垂直坐標軸的超平面將一個k維空間分割

每個節點保存了一個點,它所代表的超平面就是經過這個點垂直於某個坐標軸一個超平面

每個子樹代表了一個區域(代碼實現中是包含子樹中所有點的最小超矩形,實際上應該是劃分後的那個超矩形

怎麽做?

建樹

我沒有任何建樹,下一個

復雜度\(O(kn\log n)\),一個分治...

插入

直接插入就行了,註意一路update

挺不科學的插入的話會破壞建樹時的平衡性

所以要加入重構好麻煩不想寫

查詢

有點詭異的啟發式搜索

有一個估算一個點到一個超矩形的最大/最小距離的操作

對於最近鄰來說,先搜左右兒子中距離近的,並且只搜估算最近距離小於當前ans的

k近鄰的話,用個大根堆,一直保持堆中有k個的元素

遠的話換成遠就行了QwQ

聽說隨機數據復雜度\(O(\log n)\)\(O(n\sqrt{n})\) ,不會證不會證

代碼實現

因為早就退役了所以我也沒有做很多題來練習各種鬼畜用法的必要了扔模板就跑

bzoj2648 帶插入最近鄰

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int N = 1e6+5, inf = 1e9;
#define lc t[x].ch[0]
#define rc t[x].ch[1]
#define max0(x) max(x, 0)

int n, m;
int curD = 0;
struct meow {
    int d[2];
    meow() {}
    meow(int x, int y){d[0]=x; d[1]=y;}
    bool operator < (const meow &r) const {
        return d[curD] < r.d[curD];
    }
    int calDist(meow &a) {
        return abs(d[0] - a.d[0]) + abs(d[1] - a.d[1]);
    }
};
meow a[N];
struct node {
    int ch[2], x[2], y[2];
    meow p;
    void update(node &a) {
        x[0] = min(x[0], a.x[0]); x[1] = max(x[1], a.x[1]);
        y[0] = min(y[0], a.y[0]); y[1] = max(y[1], a.y[1]);
    }
    void set(meow &a) {
        p = a;
        x[0] = x[1] = a.d[0];
        y[0] = y[1] = a.d[1];
    }
    int evaDist(meow &a) {
        int xx = a.d[0], yy = a.d[1];
        return max0(x[0] - xx) + max0(xx - x[1]) + max0(y[0] - yy) + max0(yy - y[1]);
    }
} t[N];
int root;
int build(int l, int r, int d) {
    curD = d;
    int x = (l+r)>>1;
    nth_element(a+l, a+x, a+r+1);
    t[x].set(a[x]);
    if(l < x) lc = build(l, x-1, d^1), t[x].update(t[lc]);
    if(x < r) rc = build(x+1, r, d^1), t[x].update(t[rc]);
    return x;
}
void insert(meow q) {
    t[++n].set(q);
    for(int x=root, D=0; x; D^=1) {
        t[x].update(t[n]);
        int &nxt = t[x].ch[q.d[D] >= t[x].p.d[D]];
        if(nxt == 0) {
            nxt = n;
            break;
        }
        else x = nxt;
    }
}

int ans;
void query(int x, meow q) {
    int nowDist = t[x].p.calDist(q), d[2];
    d[0] = lc ? t[lc].evaDist(q) : inf;
    d[1] = rc ? t[rc].evaDist(q) : inf;
    int wh = d[1] <= d[0];
    ans = min(ans, nowDist);
    if(d[wh] < ans) query(t[x].ch[wh], q);
    wh ^= 1;
    if(d[wh] < ans) query(t[x].ch[wh], q);
}

int main() {
    cin >> n >> m;
    int c, x, y;
    for(int i=1; i<=n; i++) {
        scanf("%d %d", &x, &y);
        a[i] = meow(x, y);
    }
    root = build(1, n, 0);
    for(int i=1; i<=m; i++) {
        scanf("%d %d %d", &c, &x, &y);
        if(c == 1) insert(meow(x, y));
        else {
            ans = inf;
            query(root, meow(x, y));
            printf("%d\n", ans);
        }
    }
}

bzoj4520 k遠點對

每個點求一次k遠點

值得註意的是會TLE所以整體用一個大根堆才行

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <ctime>
#include <queue>
using namespace std;
typedef long long ll;
const int N = 1e5+5;
const ll inf = 1e18;
#define lc t[x].ch[0]
#define rc t[x].ch[1]
#define max0(x) max(x, 0)

inline ll sqr(ll x) {return x*x;}

int n, K;
int curD = 0;
struct meow {
    ll d[2];
    meow() {}
    meow(ll x, ll y){d[0]=x; d[1]=y;}
    bool operator < (const meow &r) const {
        //if(d[curD] == r.d[curD]) return d[curD^1] < r.d[curD^1];
        return d[curD] < r.d[curD];
    }
    ll calDist(meow &a) {
        //return abs(d[0] - a.d[0]) + abs(d[1] - a.d[1]);
        return sqr(d[0] - a.d[0]) + sqr(d[1] - a.d[1]);
    }
};
meow a[N];
struct node {
    int ch[2], x[2], y[2];
    meow p;
    void update(node &a) {
        x[0] = min(x[0], a.x[0]);
        x[1] = max(x[1], a.x[1]);
        y[0] = min(y[0], a.y[0]);
        y[1] = max(y[1], a.y[1]);
    }
    void set(meow &a) {
        p = a;
        x[0] = x[1] = a.d[0];
        y[0] = y[1] = a.d[1];
    }
    ll evaMaxDist(meow &a) {
        ll xx = a.d[0], yy = a.d[1];
        return max(sqr(x[0]-xx), sqr(x[1]-xx)) + max(sqr(y[0]-yy), sqr(y[1]-yy));
    }
} t[N];
int root;
int build(int l, int r, int d) {
    curD = d;
    int x = (l+r)>>1;
    nth_element(a+l, a+x, a+r+1);
    t[x].set(a[x]);
    if(l < x) {
        lc = build(l, x-1, d^1);
        t[x].update(t[lc]);
    }
    if(x < r) {
        rc = build(x+1, r, d^1);
        t[x].update(t[rc]);
    }
    return x;
}

priority_queue<ll, vector<ll>, greater<ll> > ans;
void query(int x, meow q) {
    ll nowDist = t[x].p.calDist(q), d[2];
    d[0] = lc ? t[lc].evaMaxDist(q) : -inf;
    d[1] = rc ? t[rc].evaMaxDist(q) : -inf;
    int wh = d[1] >= d[0];
    if(nowDist > ans.top()) ans.pop(), ans.push(nowDist);
    if(d[wh] > ans.top()) query(t[x].ch[wh], q);
    wh ^= 1;
    if(d[wh] > ans.top()) query(t[x].ch[wh], q);
}

int main() {
    cin >> n >> K; K <<= 1;
    int x, y;
    for(int i=1; i<=n; i++) {
        scanf("%d %d", &x, &y);
        a[i] = meow(x, y);
    }
    root = build(1, n, 0);
    for(int j=1; j<=K; j++) ans.push(-inf);
    for(int i=1; i<=n; i++) {
        query(root, a[i]);
    } 
    cout << ans.top() << endl;
}

K-D Tree 學習筆記