K-D Tree 学习笔记
K-D Tree 是处理
构建
要让 K-D Tree 尽量贴近一个二叉搜索树的形态,那就要让某一维度上的中位数作为根节点。对于维度的选择,有两种常见的方式:一是两个维度交替建树、二是对
插入
考虑维护平衡树的方式是难以运用到 KD-Tree 上的。于是常见的方法是二进制分组或替罪羊树重构。处于容易理解的角度,笔者一般使用替罪羊树重构的方式实现。具体地,设置一个平衡因子
这里给出较为完整的模板实现:
namespace KDT {
const db shit = 0.75;
int tot;
struct Node {
int lc, rc;
int x, y;
int l, r, u, d;
int sz, tp;
} e[N * M];
#define lc(i) e[i].lc
#define rc(i) e[i].rc
#define x(i) e[i].x
#define y(i) e[i].y
#define l(i) e[i].l
#define r(i) e[i].r
#define u(i) e[i].u
#define d(i) e[i].d
#define sz(i) e[i].sz
#define tp(i) e[i].tp
bool cmpx(int x, int y) {
return x(x) < x(y);
}
bool cmpy(int x, int y) {
return y(x) < y(y);
}
void push_up(int p) {
sz(p) = sz(lc(p)) + sz(rc(p)) + 1;
l(p) = r(p) = x(p);
u(p) = d(p) = y(p);
if (lc(p)) {
l(p) = min(l(p), l(lc(p)));
r(p) = max(r(p), r(lc(p)));
u(p) = min(u(p), u(lc(p)));
d(p) = max(d(p), d(lc(p)));
}
if (rc(p)) {
l(p) = min(l(p), l(rc(p)));
r(p) = max(r(p), r(rc(p)));
u(p) = min(u(p), u(rc(p)));
d(p) = max(d(p), d(rc(p)));
}
}
bool gotoshit(int p) {
return (db)max(sz(lc(p)), sz(rc(p))) > sz(p) * shit;
}
int id[N], cp;
void sve(int p) {
if (!p) return;
sve(lc(p));
id[++cp] = p;
sve(rc(p));
}
void build(int &p, int l, int r) {
if (l > r) return p = 0, void();
int mid = (l + r) >> 1;
db v1 = 0, v2 = 0, av1 = 0, av2 = 0;
for (int i = l; i <= r; i++) {
av1 += x(id[i]);
av2 += y(id[i]);
}
av1 /= r - l + 1, av2 /= r - l + 1;
for (int i = l; i <= r; i++) {
v1 += (av1 - x(id[i])) * (av1 - x(id[i]));
v2 += (av2 - y(id[i])) * (av2 - y(id[i]));
}
if (v1 > v2) {
nth_element(id + l, id + mid, id + r + 1, cmpx);
tp(p) = 1;
}
else {
nth_element(id + l, id + mid, id + r + 1, cmpy);
tp(p) = 2;
}
p = id[mid];
build(lc(p), l, mid - 1);
build(rc(p), mid + 1, r);
push_up(p);
}
void forashit(int &p) {
cp = 0;
sve(p);
build(p, 1, cp);
}
void insert(int &p, int x, int y) {
if (!p) {
p = ++tot;
x(p) = x, y(p) = y;
return push_up(p);
}
if (tp(p) == 1) {
if (x <= x(p)) insert(lc(p), x, y);
else insert(rc(p), x, y);
}
else {
if (y <= y(p)) insert(lc(p), x, y);
else insert(rc(p), x, y);
}
push_up(p);
if (gotoshit(p)) forashit(p);
}
}
查询
可以证明 K-D Tree 查询矩形范围内维护的问题复杂度是单次
int fmx(int p, int x, int y) {
int ans = 0;
ans += max(abs(x - l(p)), abs(x - r(p)));
ans += max(abs(y - u(p)), abs(y - d(p)));
return ans;
}
int fmn(int p, int x, int y) {
int ans = 0;
if (x < l(p)) ans += l(p) - x;
if (x > r(p)) ans += x - r(p);
if (y < u(p)) ans += u(p) - y;
if (y > d(p)) ans += y - d(p);
return ans;
}
int amx, amn;
void qmx(int p, int x, int y) {
if (!p) return;
amx = max(amx, abs(x - x(p)) + abs(y - y(p)));
int vl = -inf, vr = -inf;
if (lc(p)) vl = fmx(lc(p), x, y);
if (rc(p)) vr = fmx(rc(p), x, y);
if (vl > vr) {
if (vl > amx) qmx(lc(p), x, y);
if (vr > amx) qmx(rc(p), x, y);
}
else {
if (vr > amx) qmx(rc(p), x, y);
if (vl > amx) qmx(lc(p), x, y);
}
}
void qmn(int p, int x, int y) {
if (!p) return;
if (!(x(p) == x && y(p) == y)) amn = min(amn, abs(x - x(p)) + abs(y - y(p)));
int vl = inf, vr = inf;
if (lc(p)) vl = fmn(lc(p), x, y);
if (rc(p)) vr = fmn(rc(p), x, y);
if (vl < vr) {
if (vl < amn) qmn(lc(p), x, y);
if (vr < amn) qmn(rc(p), x, y);
}
else {
if (vr < amn) qmn(rc(p), x, y);
if (vl < amn) qmn(lc(p), x, y);
}
}
分类:
学习笔记
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律