KD Tree学习笔记
KD Tree 是一种用于多维数点的数据结构(一般是二维)。它并不特别复杂,本质其实很暴力,和替罪羊树与线段树都比较类似。
大致思路:每次把点分为尽可能平衡的两半,然后通过类似线段树的方式拆分查询区间,以此控制复杂度。
算法流程
构建:
如果已经给定了一些元素(k维),构建kdtree采用以下的方式:
首先,将当前区间按照第d维的中间数左右分段。d每分一层便换到下一个维度,直观理解就是:每次将这些点转个方向切一刀,切得不平衡(点大量集中在刀口上)的概率便会小一些。
这里需要用到stl里的nth_element
函数,用法如下:
int mid = (l + r) >> 1;
nth_element(a + l, a + mid, a + r + 1, cmp);
其中cmp为比较函数。
这个函数的作用是将第 m i d mid mid个位置上放上按照cmp排好序的第 m i d mid mid字符,同时保证 m i d mid mid左边的数都比它小,右边的都比它大(等于的情况会随机分布,不需要考虑)。这个函数完美满足了我们的要求,因为我们其实不需要将它完全排好序,只要将元素按照d维下的坐标切成两段即可。
然后将 m i d mid mid位置上的元素填入树中,最后左右区间递归构建。
插入:
和 B S T BST BST一样,从根向下比较,一直落到叶子节点为止。
但是!如果事情这么简单就好了。
就像 B S T BST BST一旦有插入就势必会带来不平衡一样, k d t r e e kdtree kdtree也一样。碰到毒瘤出题人很容易被卡到n方。这时我们就要使用替罪羊树的重构法,遇到太不平衡的节点就将整颗子树暴力重构。这里并不太难,如果没学过替罪羊树可以查阅相关资料或者参考代码。
查询:
较为灵活,随机应变。
大致思路是,如果区间中可能存在对答案有贡献的元素就查询下去。
删除:
从没用过
复杂度:
随机 n l o g n nlogn nlogn
但是构建数据可以被卡成 n s q r t ( n ) nsqrt(n) nsqrt(n)
例题:
【模板】三维偏序
【国家集训队】JZPFAR
【CQOI2016】K远点对
简单题
【CH弱省胡策R2】TATT
细节真的很多,注意以下几点:
- 注意重构时要用的size不是子树值的和而是子树点数和(洛谷上二十发二十分就是我)
- 注意下标01一定不能弄错
- 注意判断条件
- 注意如果需要通过对某一维排序进行降维,一定要先建树后排序
- 注意一些其他的问题
全是血淋淋的教训啊
这里给出简单题一题的代码。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 200005;
const double P = 0.75;
int read()
{
int q = 0, w = 1;
char ch = 'p';
while(ch != '-'&& (ch < '0' || ch > '9')) ch = getchar();
if(ch == '-') w = -1, ch = getchar();
while(ch >= '0' && ch <=