【数据结构与算法】K 近邻算法—— KD 树 算法原理讲解和C语言实现代码
1. KD树原理讲解
KD树(k-dimensional tree)是一种用于高维空间搜索的数据结构,尤其用于给定查询点时搜索最近邻或其他范围搜索任务。与K近邻算法中的线性搜索相比,KD树可以显著加快许多空间搜索任务的速度。
K-D树是将K维空间中的点进行分割的数据结构,D是dimensional(维度)的缩写,K-D树是BSP(Binary Space Partitioning)的一种。
简单的说,K-D树就是K维空间的二分查找树,是二分查找树在K维空间的泛化。
还是有点抽象。举个例子:
我们都知道二分查找树(BST)这样基础的数据结构,它是基于二分查找的思想实现 O ( l o g 2 N ) O(log_2N) O(log2N)的插入和查找速度。
如果把BST中的所有元素看成一维线段上的所有点,从树的根节点开始每个节点都会把线段分成两段。

所以,BST本质上就是一维K-D树(或者叫做1-D树)。
现在将树中的元素推广到二维平面上的点,树的每一层按照维度轮流划分。比如,奇数层按x轴划分,偶数层按y轴划分,这样就得到一棵二维K-D树(2-d树):

同样我们可以将元素推广到三维空间中的点,那样我们可以得到一棵3d-tree。

理论上k-d树可以继续到4维,5维… 作为一个三维空间里的生物,就没法用图来展示这样的元素了😅.
这里有一个二维K-D树的模拟程序能帮助我们更好的理解K-D树:

任何事物都有好坏的地方,K-D树也是如此。
K-D树的优点:
很明显,K-D树和BST一样不仅可以精确查找,也更适合做范围查询,但K-D树比BST更强,它能对多个维度进行范围查询。
比如:
Person1(age:18,hight:176),Person2(age:19,height:181) … Person10(age:23,height:171)
想要查找年龄在20岁以上并且身高在170到180之间的所有人,用K-D树就能很好的解决。

由于K-D树的结构要求,上面的例子中要求每个人的年龄和身高各项数据必须齐全,如果某个人只有年龄或只有身高,就无法使用K-D树索引了。所以实际上K-D树更常见的应用是经纬度定位,或者三维空间定位(某个维度数据缺失,其他维度数据也就没有意义了)
缺点:
和二分查找树一样,K-D树也有平衡性问题。
高纬度数据查找效率并不一定好,有时候可能不如最原始的暴力查找。
KD树是一种二叉树,其中每个节点表示数据集中的一个点。KD树的构造过程包括:
- 依次选择各维度作为分割维度,将数据集排序;
- 选择排序后中间元素作为根节点;
- 根据分割平面,将数据分为两部分:一部分位于当前分割维度的左侧,另一部分位于右侧;
- 重复这个过程,直到所有数据点都被构造成树的各个节点。
在KD树结构中查找最近邻的方法是:
- 从根节点开始,不断向下查找,将查询点与子节点的分割平面进行比较,根据比较结果前进到相应的子树,直到达到叶子节点;
- 回溯树结构,寻找更短的距离;
- 当回溯结束后,返回最近的节点。
2. KD树的C语言实现
以下是一个简化的KD树C语言实现示例。这里的实现仅包括KD树的构建部分,要让它完整,还需要添加用于搜索最近邻的功能。
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int data[2];
struct Node* left;
struct Node* right;
} Node;
int compare(int dim, const void* a, const void* b) {
return ((int*)a)[dim] - ((int*)b)[dim];
}
Node* buildKDTree(int data[][2], int n, int depth) {
if (n <= 0) return NULL;
int dim = depth % 2;
qsort(data, n, 2*sizeof(int), compare(dim, a, b));
Node* node = (Node*)malloc(sizeof(Node));
node->data[0] = data[n/2][0];
node->data[1] = data[n/2][1];
node->left = buildKDTree(data, n/2, depth + 1);
node->right = buildKDTree(data + n/2 + 1, n - n/2 - 1, depth + 1);
return node;
}
int main() {
int points[][2] = {{3, 6}, {17, 15}, {13, 15}, {6, 12}, {9, 1}, {2, 7}, {10, 19}};
int n = sizeof(points) / sizeof(points[0]);
Node* root = buildKDTree(points, n, 0);
return 0;
}
上述代码实现了KD树结构的构建,可以自行扩展,以实现最近邻查询的代码。
更多推荐
所有评论(0)