KD-tree的原理以及构建与查询操作的python实现

前几天小组讨论会上展示了kd-tree(k-dimension tree),感觉这玩意儿还挺有用的,所以学习了一下它的原理,然后把其中的构建kd-tree以及对应的查询操作实现了一下,现在跟大家分享一下

首先说一下什么是kd-tree把

不过首先得说一下bst(二叉查找树),递归定义如下:如果左子树上的节点存储的数值都小于根节点中存储的数值,并且右子树上的节点存储的数值都大于根节点中存储的数值,那么这样的二叉树就是一颗二叉查找树

有了bst的概念,那么kd-tree就 容易理解多了,首先kd-tree的节点中存储的数值是一个k维的数据点,而bst的节点中存储的可以视为是1维的数据点,kd-tree与bst不同的地方在于进行分支决策的时候,还需要选择一个维度的值进行比较,选择哪个维度呢?每个节点还需要维护一个split变量,表示进行分支决策的时候,选择哪个维度的值进行比较,现在给出一个kd-tree节点的定义

class KD_node:
    def __init__(self, point=None, split=None, LL = None, RR = None):
        """
        point:数据点
        split:划分域
        LL, RR:节点的左儿子跟右儿子
        """
        self.point = point
        self.split = split
        self.left = LL
        self.right = RR

point就代表节点存储的k维数据点,left,right分别代表指向左右儿子的指针,split代表划分维度,在节点进行划分之前,我们需要确定划分维度,那么怎么选择划分维度呢,这又要从kd-tree的用途开始说起了

kd-tree是一种对高维空间的数据点进行划分的特殊数据结构,主要应用就是高维空间的数据查找,如:范围搜索和K近邻(knn)搜索,范围搜索就是给定查询点和距离阈值,获取在阈值范围内的所有数据点;knn搜索就是给定查询点和搜索点的数目n,查找出到搜索点最近的n个点的数目;

以上这两种搜索如果通过传统方法来实现,那么最坏情况下可能会穷举 数据急中的所有点,这种方法的缺点就是完全没有利用到数据集中蕴藏的结构信息,当数据点很多时,搜索效率不高;

事实上,实际数据集中的点一般时呈簇状分布的,所以,很多点我们是完全没有必要遍历的,索引树的方法就是对将要搜索的点进行空间划分,空间划分可能会有重叠,也可能没有重叠,kd-tree就是划分空间没有重叠的索引树

这样说可能有一点乱,那我还是以“二分查找”作为引入吧

如果给你一组数据   9 1 4 7 2 5 0 3 8

让你查找8,如果你挨个查找,那么将会把数据集都遍历一遍,

如果你排一下序那现在数据集就变成了:0 1 2 3 4 5 6 7 8 9,其实我们进行了很多没有必要的查找,

如果我以5为分界点,那么数据点就被分为了 两个“簇” (0 1 2 3 4)和(6 7 8 9),如

### 关于KD-TreePython实现 以下是基于已有引用和专业知识构建的一个简单的KD-Tree Python实现代码: ```python import numpy as np class Node: def __init__(self, point=None, axis=None, left=None, right=None): self.point = point # 节点存储的数据点 self.axis = axis # 切分维度 self.left = left # 左子节点 self.right = right # 右子节点 def build_kdtree(points, depth=0): if not points: return None # 获取当前切分维度 k = len(points[0]) axis = depth % k # 对数据按照当前维度排序并取中间值作为分割点 sorted_points = sorted(points, key=lambda point: point[axis]) median_index = len(sorted_points) // 2 median_point = sorted_points[median_index] # 构建当前节点及其左右子树 node = Node(point=median_point, axis=axis) node.left = build_kdtree(sorted_points[:median_index], depth + 1) node.right = build_kdtree(sorted_points[median_index + 1:], depth + 1) return node def nearest_neighbor(node, target, depth=0, best_node=None): if node is None: return best_node # 更新最佳匹配点 distance_to_best = np.linalg.norm(np.array(target) - np.array(best_node.point)) if best_node else float('inf') distance_to_current = np.linalg.norm(np.array(target) - np.array(node.point)) if distance_to_current < distance_to_best: best_node = node # 当前维度 k = len(target) axis = depth % k # 进入下一层搜索 next_branch = None opposite_branch = None if target[axis] < node.point[axis]: next_branch = node.left opposite_branch = node.right else: next_branch = node.right opposite_branch = node.left # 搜索较近的一侧 best_node = nearest_neighbor(next_branch, target, depth + 1, best_node) # 计算超球体分裂面的距离,判断是否需要进入另一侧 distance_to_hyperplane = abs(target[axis] - node.point[axis]) if distance_to_hyperplane < distance_to_best: best_node = nearest_neighbor(opposite_branch, target, depth + 1, best_node) return best_node # 测试用例 points = [[2, 3], [5, 4], [9, 6], [4, 7], [8, 1], [7, 2]] root = build_kdtree(points) target = [6, 3] nearest = nearest_neighbor(root, target) print(f"Nearest neighbor to {target} is {nearest.point}") ``` 上述代码实现了基本的KD-Tree结构以及最近邻查询功能。其中`build_kdtree`函数用于递归地创建树结构[^1],而`nearest_neighbor`则通过递归来完成最邻近点的查找操作。 对于更高效的实现方式或者更大规模的应用场景,可以考虑使用Scipy库中的现成工具来简化开发过程[^2]。 #### 更多参考资料 除了自定义实现外,还可以参考其他成熟的开源项目或文档进一步学习如何优化性能及扩展功能][^[^34]。
评论 12
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值