前言
一个感觉特别单一的数据结构啊,Po大爷才写两个这个题呢。
然后仔细研究了一下KD-TREE为什么要这样做,发现其中的每一个操作都是很巧妙的。
原理简述(瞎逼逼)
KD-Tree确实应该算是计算几何的一种东西了吧,可以看作线段树的高维推广还自带离散功能。
KD-Tree也是一个二叉树,并且除了叶子结点外,每个结点都代表了一堆点,叶子结点则代表了一个,和线段树是很像的。
每个点我们要维护它的各个坐标的最大值以及最小值,这是有利于后面的搜索操作的。
它的分割原则是对于一个K维的一些点,用一个超平面(k-1维)来把这堆点分割成平均的两部分,如何平均,用一些STL的nth_element函数就可以了(下面有介绍)
在分割的时候,我们根据不同的层数,不停的转变被分割的那一维,本来一直用一维也是可以的,但是对于接下来的搜索操作就不是很有利了。
现在讲一下一个搜索操作:对于一个结点,我们要找最大值/最小值,就按照我们对于一堆点坐标最值来贪心地去选择顺序搜索,并且及时剪枝,所以这似乎就是一个优雅的暴力。不过权威证明,它的时间复杂度是O(n^1/2),并且在实际运行中跑得贼快。这里也可以看我们为什么要不停换维度分割,我们让一堆点尽量存在在一个比较‘方’的区间里面,有利于我们的搜索。(仔细体会一下嘛)
插入操作就按照划分方式往下放就行了,但是由于插入过多可能不平衡,所以我们可以利用替罪羊树的思想在一定情况下进行暴力重构。
时间复杂度的话:
建树是O(nlogn)
其它大概都是O(logn)
搜索是O(n^(1-(1/k)))但是实际效果好很多
再谈KD-Tree
KD-Tree的时间复杂度随着维度升高并不是线性增长的,一般二维还是sqrt(n)的级别,那么和log^2(n),基本上是没有差距的,但是更高纬度可能就不行了,所以最好是保证在二维之内,KDTree的维护有两个方式,一个是暴力重构,一个是一来就把数据全部建好。
然而KD-Tree找距离是可以被卡成O(n)的,但是毒瘤出题人要是偏要故意卡那就认了呗。不过一般数据结构不知道能不能卡。
数据的限制一般是很难降维的,毕竟咱不是三体,所以一旦分析除了,最好不要尝试降维,而是找合适的数据结构。
KD-Tree查找时间复杂度的由来:由于为了贪心查找距离,所以我们要求当前空间尽量方,这样比较好找,但是就会出现一个区间本来都抵满了还要被继续分割的情况,所以是比logn^2要大一些的,而且维度越高越大。
关于维度的选择,不要觉得只是拿来玩的,由于不同维度可能有不同的性质,比如区间加法或者,
所以遇到二维以内的在线问题就直接上KD-Tree吧, 也就是说懒得去写树套树了,但是维度更高就尝试用其它方法降维,比如二分、可持久化,如果可以用莫队,CDQ什么的也不要用KD-Tree,因为KD-Tree时间复杂度不好保证。
注意二分也是可以降维的东西。
一般来说sqrt(n)的算法都是可以代替两个log的,所以懒的话就多考虑下sqrt(n)算法吧。
介绍一个STL函数:nth_element
用法介绍:nth_element(a+1,a+n,a+m+1)
含义:就是把a数组第n大的元素放在第n个位置,并且比n小的在n前面,比n大的在n后面
时间复杂度:元素较多大概是O(n),较少/最坏时间复杂度是O(n^2),是一种很高效的算法了。