文章目录
0 什么是KD-Tree?
- K D KD KD 树( K − D i m e n s i o n K-Dimension K−Dimension T r e e Tree Tree)是一种对 K K K 维空间中的实例点进行存储以便对其进行快速检索的树形数据结构
- K D KD KD 树是是一种二叉树,表示对 K K K 维空间的一个划分,构造 K D KD KD 树相当于不断地用垂直于坐标轴的超平面将 K K K 维空间切分,构成一系列的 K K K 维超矩形区域, K D KD KD 树的每个结点对应于一个 K K K 维超矩形区域
- 利用 K D KD KD 树可以省去对大部分数据点的搜索,从而减少搜索的计算量
1 构建 K D − T r e e KD-Tree KD−Tree
- 设置两个坐标轴,横轴称为
X
(
1
)
X(1)
X(1) 轴,竖轴称为
X
(
2
)
X(2)
X(2) 轴
- 下图就是根据左边的划分来构建的一颗
K
D
−
T
r
e
e
KD-Tree
KD−Tree
- 如果下方还有 X ( 2 ) X(2) X(2) 的点,就继续循环
- K D − T r e e KD-Tree KD−Tree 就是循环往复的
- X ( 1 ) X(1) X(1) 到 X ( 2 ) X(2) X(2) 再到 X ( 1 ) X(1) X(1) 再到 X ( 2 ) X(2) X(2) 一直循环
1.1.1 详细步骤
- 现在 X ( 1 ) X(1) X(1) 和 X ( 2 ) X(2) X(2) 坐标轴上将这 6 6 6 个点都标上去,再根据中位数原则一步步构造新的超平面进行切分
- 首先该怎么切呢?先以 X ( 1 ) X(1) X(1) 为标准,首先以第一个坐标值的中位数为切分标准的,什么意思呢?看下图
- 有 X ( 1 ) X(1) X(1) 坐标的一共出现 6 6 6 个点,那么这 6 6 6 个点的第一个坐标值中位数就有两个点,也就是 ( 5 , 4 ) (5, 4) (5,4) 以及 ( 7 , 2 ) (7, 2) (7,2) ,这样求一下 X ( 1 ) X(1) X(1) 方向上的中位数,也就是 6 6 6 ,找到离 6 6 6 最近的那个点, 5 5 5 和 7 7 7 都可以,这个地方选择的是 ( 7 , 2 ) (7, 2) (7,2) 这个点
- 以 ( 7 , 2 ) (7, 2) (7,2) 这个点作为切点来做一个超平面
- 如下图,这样就切好了,垂直于
X
(
1
)
X(1)
X(1) 轴进行竖向切分,同时取这个节点作为
K
D
−
T
r
e
e
KD-Tree
KD−Tree 的第一个根节点
- 接下来继续切,就不是垂直于 X ( 1 ) X(1) X(1) 轴了,因为之前说过这是循环往复的,所以现在应该是垂直于 X ( 2 ) X(2) X(2) 轴的,这样横着切,刚刚新作的超平面已经将空间变成了左右两个矩形的子空间,现在我们再对左边的这个子空间进行切分,切分的时候注意,如原始图,左边的子空间就不包含刚刚的 ( 7 , 2 ) (7, 2) (7,2) 这个点了,而是只包含 ( 2 , 3 ) (2, 3) (2,3)、 ( 4 , 7 ) (4, 7) (4,7)、 ( 5 , 4 ) (5, 4) (5,4) 这三个点,所以还是依据刚刚的中位数原则,只不过现在是以 X ( 2 ) X(2) X(2) 轴为标准,那么 7 , 4 , 3 7,4,3 7,4,3 离中位数最近的点肯定是 4 4 4 了,所以以 4 4 4 这个点 ( 5 , 4 ) (5, 4) (5,4) 进行垂直于 X ( 2 ) X(2) X(2) 轴的横向切分
- 这样,左边的子空间就分为左上子子空间以及左下子子空间
- 同时把 ( 5 , 4 ) (5, 4) (5,4) 加入节点
- 由于左上子子空间就剩下 ( 4 , 7 ) (4, 7) (4,7) 自己一个数了,所以现在根据 X ( 1 ) X(1) X(1) 轴中位数切分准则,其自己就是自己的中位数,作一条垂直于 X ( 1 ) X(1) X(1) 的超平面进行切分
- 因为是平衡
K
D
−
T
r
e
e
KD-Tree
KD−Tree 所以,
(
4
,
7
)
(4, 7)
(4,7) 这个节点就应该放在其根节点
(
5
,
4
)
(5, 4)
(5,4) 的右子树上(以
X
(
1
)
X(1)
X(1) 为基准,
(
2
,
3
)
(2, 3)
(2,3) 中的
2
2
2 是要小于
(
4
,
7
)
(4, 7)
(4,7) 当中的
4
4
4 的,左小右大,平衡
K
D
−
T
r
e
e
KD-Tree
KD−Tree)
- 左下子子空间同理,只剩下 ( 2 , 3 ) (2, 3) (2,3) 这个点了,所以直接作一个垂直于 X ( 1 ) X(1) X(1) 的超平面进行切分,同时 ( 2 , 3 ) (2, 3) (2,3) 节点放到 ( 5 , 4 ) (5, 4) (5,4) 节点的左子树上
- 再切右边空间,右边只剩下 ( 9 , 6 ) (9, 6) (9,6) 以及 ( 8 , 1 ) (8, 1) (8,1) 两个数据点了,因为循环往复,现在是以 X ( 2 ) X(2) X(2) 的中位数为标准了,算出来中位数是 2.5 2.5 2.5,所以以这两个节点哪个节点为切点都可以,因为 6 − 3.5 = 3.5 − 1 6 - 3.5 = 3.5 - 1 6−3.5=3.5−1,这里选择的是以 ( 9 , 6 ) (9, 6) (9,6) 为切点,同时将 ( 9 , 6 ) (9, 6) (9,6) 加入 K D − T r e e KD-Tree KD−Tree
- 之后 ( 8 , 1 ) (8, 1) (8,1) 就按照 X ( 1 ) X(1) X(1) 的切分规则竖着切,这样就切分完毕,同时 K D − T r e e KD-Tree KD−Tree 也构建完成
2 搜索 K D − T r e e KD-Tree KD−Tree
- 举个栗子,查找 ( 3 , 4.5 ) (3, 4.5) (3,4.5) (红色的点)的最邻近点
- 递归+回溯的思想
- 再加上刚刚的左小右大的特点
- 进行二叉查找,先从 ( 7 , 2 ) (7, 2) (7,2) 查找到 ( 5 , 4 ) (5, 4) (5,4) 节点,在进行查找时是由 y = 4 y=4 y=4 为分割超平面的,由于查找点为 y y y 值为 4.5 4.5 4.5,因此进入右子空间查找到 ( 4 , 7 ) (4, 7) (4,7) 形成搜索路径: ( 7 , 2 ) → ( 5 , 4 ) → ( 4 , 7 ) (7, 2)→(5, 4)→(4, 7) (7,2)→(5,4)→(4,7),取 ( 4 , 7 ) (4, 7) (4,7) 为当前最近邻点。以目标查找点为圆心,目标查找点到当前最近点的距离 2.69 2.69 2.69 为半径确定一个红色的圆。然后回溯到 ( 5 , 4 ) (5, 4) (5,4),计算其与查找点之间的距离为 2.06 2.06 2.06,则该结点比当前最近点距目标点更近以 ( 5 , 4 ) (5, 4) (5,4) 为当前最近点。用同样的方法再次确定一个绿色的圆,可见该圆和 y = 4 y=4 y=4 超平面相交,所以需要进入 ( 5 , 4 ) (5, 4) (5,4) 结点的另一个子空间进行查找。 ( 2 , 3 ) (2, 3) (2,3) 结点与目标点距离为 1.8 1.8 1.8,比当前最近点要更近,所以最近邻点更新为 ( 2 , 3 ) (2, 3) (2,3),最近距离更新为 1.8 1.8 1.8,同样可以确定一个蓝色的圆。接着根据规则回退到根结点 ( 7 , 2 ) (7, 2) (7,2),蓝色圆与 x = 7 x=7 x=7 的超平面不相交,因此不用进入 ( 7 , 2 ) (7, 2) (7,2) 的右子空间进行查找。至此,搜索路径回溯完,返回最近邻点 ( 2 , 3 ) (2, 3) (2,3),最近距离 1.8 1.8 1.8。
3 总结
- 以上就是 K D − T r e e KD-Tree KD−Tree 算法的核心