本文始发于个人公众号:TechFlow,原创不易,求个关注
今天是机器学习的第15篇文章,之前的文章当中讲了Kmeans的相关优化,还讲了大名鼎鼎的EM算法。有些小伙伴表示喜欢看这些硬核的,于是今天上点硬菜,我们来看一个机器学习领域经常用到的数据结构——KD-Tree。
从线段树到KD树
在讲KD树之前,我们先来了解一下线段树的概念。线段树在机器学习领域当中不太常见,作为高性能维护的数据结构,经常出现在各种算法比赛当中。线段树的本质是一棵维护一段区间的平衡二叉树。
比如下图就是一个经典的线段树:
从下图当中我们不难看出来,这棵线段树维护的是一个区间内的最大值。比如树根是8,维护的是整个区间的最大值,每一个中间节点的值都是以它为树根的子树中所有元素的最大值。
通过线段树,我们可以在 O ( l o g N ) O(logN) O(logN)的时间内计算出某一个连续区间的最大值。比如我们来看下图:
当我们要求被框起来的区间中的最大值,我们只需要找到能够覆盖这个区间的中间节点就行。我们可以发现被红框框起来的两个节点的子树刚好覆盖这个区间,于是整个区间的最大值,就是这两个元素的最大值。这样,我们就把一个需要 O ( n ) O(n) O(n)查找的问题降低成了 l o g ( N ) log(N) log(N),不但如此,我们也可以做到 O ( l o g N ) O(logN) O(logN)复杂度内的更新,也就是说我们不但可以快速查询,还可以更新线段当中的元素。
当然线段树的应用非常广泛,也有许多种变体,这里我们不过多深入,感兴趣的同学可以期待一下周三的算法与数据结构专题,在之后的文章当中会为大家分享线段树的相关内容。在这里,我们只需要有一个大概的印象,线段树究竟完成的是什么样的事情即可。
线段树维护的是一个线段,也就是区间内的元素,也就是说维护的是一个一维的序列。如果我们将数据的维度扩充一下,扩充到多维呢?
是的,你没有猜错,从某种程度上来说,我们可以把KD-Tree看成是线段树拓展到多维空间当中的情况。
KD-Tree定义
我们来看一下KD-Tree的具体定义,这里的K指的是K维空间,D自然就是dimension,也就是维度,也就是说KD-Tree就是K维度树的意思。
在我们构建线段树的时候,其实是一个递归的建树过程,我们每次把当前的线段一分为二,然后用分成两半的数据分别构建左右子树。我们可以简单写一下伪代码,来更直观地感受一下:
class Node:
def __init__(self, value, lchild, rchild):
self.value = value
self.lchild = lchild
self.rchild = rchild
def build(arr):
n = len(arr):
left, right = arr[: n//2], arr[n//2:]
lchild, rchild = self.build(left), self.build(right)
return Node(max(lchild.value, rchild.value), lchild, rchild)
我们来看一个二维的例子,在一个二维的平面当中分布着若干个点。
我们首先选择一个维度