KD树的C++实现

理论介绍

kd树(K-dimension tree)是一种对k维空间中的实例点进行存储以便对其进行快速检索的树形数据结构。kd树是是一种二叉树,表示对k维空间的一个划分,构造kd树相当于不断地用垂直于坐标轴的超平面将K维空间切分,构成一系列的K维超矩形区域。kd树的每个结点对应于一个k维超矩形区域。利用kd树可以省去对大部分数据点的搜索,从而减少搜索的计算量。

  • kd树的算法步骤
    这里写图片描述
     
  • kd树可以较大的减少搜索空间,提高搜索效率,在knn,光线追踪这些需要大量搜索的算法中具有重要的应用前景。

算法实现

  • 使用C++ 对kd树进行了实现,没有使用模板,如有错误欢迎指出~
#pragma once
#include <iostream>
#include <vector>
#include <algorithm>
#include <fstream>
using namespace std;

int dimUsed;

// kd树的节点定义
struct KdNode
{
    KdNode * parent;
    KdNode * leftChild;
    KdNode * rightChild;
    vector<int> val;    //存储的数据
    int axis;       // 使用的轴
    KdNode(vector<int> data, int ax)
    {
        val = data;
        axis = ax;
        parent = NULL;
        leftChild = NULL;
        rightChild = NULL;
    }
};

// 用于以第n位进行比较,方便重载
bool cmp(vector<int> a, vector<int> b)
{
    if (a[dimUsed] < b[dimUsed])
        return true;
    return false;
}

ostream & operator<<(ostream & os, vector<int> vi)
{
    os << "(";
    for (int i = 0; i < vi.size(); i++)
        cout << vi[i] << ",";
    os << ")";
    return os;
}

// 导入数据,第一行是数据数量和维度,后面跟着num行dim维度的数据,每个一行
bool loadData(string filename, vector<vector<int> > &data)
{
    ifstream infs(filename);
    if (infs.is_open())
    {
        int num,dim;
        infs >> num>>dim;
        vector<int> d(dim);
        for (int i = 0; i < num; i++)
        {
            for (int j = 0; j < dim; j++)
                infs >> d[j];
            data.push_back(d);
        }
        return true;
    }
    return false;
}

// 计算N维向量距离
int disVector(vector<int> a, vector<int> b)
{
    int sum = 0;
    for (int i = 0; i < a.size(); i++)
        sum += (a[i] - b[i])*(a[i] - b[i]);
    return sum;
}

// kd 树的类
class KdTree
{
private:
    int dimension;
    vector<vector<int> > data;
    KdNode * root;
public:
    KdTree(vector<vector<int> > d, int dim)
    {
        dimension = dim;
        data = d;
    }
    void createTree()
    {
        // 递归建树
        root = createTreeNode(0, data.size()-1,0);
    }

    // create Kd Tree struct
    KdNode * createTreeNode(int left, int right,int dim)
    {
        if (right < left)
            return NULL;
        dimUsed = dim;
        // 按照k维进行排序
        sort(data.begin() + left, data.begin() + right+1, cmp);
        int mid = (left + right+1) / 2;
        KdNode * r = new KdNode(data[mid], dim);
        r->leftChild = createTreeNode(left, mid - 1, (dim + 1) % dimension);
        if (r->leftChild != NULL)
            r->leftChild->parent = r;
        r->rightChild = createTreeNode(mid + 1, right, (dim + 1) % dimension);
        if (r->rightChild != NULL)
            r->rightChild->parent = r;
        return r;
    }

    void printKdTree()
    {
        printKdTreeNode(root);
    }

    void printKdTreeNode(KdNode * r)
    {
        if (r == NULL)
            return;
        printKdTreeNode(r->leftChild);
        cout << r->val << "\t";
        printKdTreeNode(r->rightChild);
    }

    // 查找kd树
    KdNode * searchKdTree(vector<int> d)
    {
        int dim = 0,minDis = 10000000;
        KdNode * r = root;
        KdNode * tmp;
        while (r != NULL)
        {
            tmp = r;
            if (d[dim] < r->val[dim])                           
                r = r->leftChild;   
            else
                r = r->rightChild;
            dim = (dim + 1) % dimension;
        }
        // 找到属于的那个节点
        minDis = min(disVector(d, tmp->val),minDis);
        KdNode * nearNode = tmp;
        cout << endl<<"nearest node: "<<tmp->val << endl;
        // 退回到根节点
        while (tmp->parent != NULL)
        {
            tmp = tmp->parent;
            // 判断父节点是否更近,如果近,更新最近节点
            if (disVector(tmp->val, d) < minDis)
            {
                nearNode = tmp;
                minDis = disVector(tmp->val, d);
            }
            cout << "now parent node: " << tmp->val << endl;
            KdNode * son;
            // 判断当前轴与点的距离,如果小于minDis,则进行到另一半进行查找
            if (abs(tmp->val[tmp->axis] - d[tmp->axis]) < minDis)
            {
                if (tmp->val[tmp->axis] > d[tmp->axis])
                    son = tmp->rightChild;
                else
                    son = tmp->leftChild;
                searchKdTreeNode(d, minDis, nearNode, son);
            }
        }   
        // 对根节点的另外半边子树进行搜索
        /*if (abs(tmp->val[tmp->axis] - d[tmp->axis]) < minDis)
        {
            if (tmp->val[tmp->axis] > d[tmp->axis])
                tmp = tmp->rightChild;
            else
                tmp = tmp->leftChild;
            searchKdTreeNode(d, minDis, nearNode, tmp);
        }*/
        return nearNode;
    }

    // 查找当前节点下的最近点
    void searchKdTreeNode(vector<int> d,int &minDis,KdNode * &nearNode,KdNode * tmp)
    {
        // 递归终止
        if (tmp == NULL)
            return;
        cout << "now node: " << tmp->val << endl;
        // 判断当前节点是否小于
        if (disVector(tmp->val, d) < minDis)
        {
            minDis = disVector(tmp->val, d);
            nearNode = tmp;
        }
        // 如果轴与节点的距离小于minDis,则两个半边都需要搜索,否则只需要搜索一个半边
        if (abs(tmp->val[tmp->axis] - d[tmp->axis]) < minDis)
        {
            searchKdTreeNode(d, minDis, nearNode, tmp->leftChild);
            searchKdTreeNode(d, minDis, nearNode, tmp->rightChild);
        }
        else
        {
        // 选择搜索的一个半边
            if (tmp->val[tmp->axis] > d[tmp->axis])
                searchKdTreeNode(d, minDis, nearNode, tmp->leftChild);
            else
                searchKdTreeNode(d, minDis, nearNode, tmp->rightChild);
        }

    }

};

// 测试kd树
void testKdTree()
{
    vector<vector<int> > data;
    loadData("kd.txt", data);
    KdTree * kdtree = new KdTree(data, data[0].size());
    kdtree->createTree();
    kdtree->printKdTree();
    cout << endl;
    vector<int> vi(2);
    cin >> vi[0] >> vi[1];
    KdNode * r = kdtree->searchKdTree(vi);
    cout << r->val << endl;
}

/*
测试数据
6 2
2 3
5 4
9 6
4 7
8 1
7 2
*/

github地址

如有错误,欢迎指出~

  • 6
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 可以回答这个问题。kd树搜索算法是一种高效的数据结构,用于解决多维空间中的最近邻搜索问题。它通过将空间划分为多个子空间,然后递归地构建kd树实现搜索。在搜索时,它可以快速地定位到目标点所在的叶子节点,并在该节点的邻居中查找最近邻点。 ### 回答2: KD树是一种用于点集搜索数据结构。它是一种二叉树,每个节点代表一个k维的点。树的构建过程是根据点集分割空间,并将每个点作为节点插入到树中。 构建KD树的过程大致如下: 1. 选择分割平面:根据算法选择一个维度作为分割的平面。 2. 判断中位数:对于选定的维度,计算点集在该维度上的中位数。 3. 通过中位数划分点集:将点集根据中位数划分成小于中位数和大于中位数的两个子集。 4. 递归构建子树:对于划分出来的子集,递归地进行构建子树的过程。 在树的每个节点中,存储了一个k维点的坐标,以及指向左子树和右子树的指针。通过这样的结构,我们可以方便地对点集进行搜索。 当需要对KD树进行搜索时,可以采用以下算法: 1. 从根节点开始,找到最近的子节点作为当前最近点。 2. 在当前子节点的同一维度上沿着树向下搜索,直到叶子节点。 3. 更新当前最近点,如果当前节点更近则更新。 4. 回溯,判断当前节点的另一个子节点是否需要搜索。 5. 如果需要搜索则跳到2,否则结束搜索。 这样的搜索算法可以快速地找到给定点集中距离目标点最近的邻居点。同时,因为KD树对空间进行了分割,可以有效减少搜索过程中的计算量。 总之,KD树是一种用于点集搜索的高效算法,它通过对空间的划分构建了一个二叉树的数据结构,通过判断距离和回溯的方式进行搜索,可以快速找到距离目标点最近的邻居点。 ### 回答3: kd树搜索算法是一种高效的搜索算法,用于在高维空间中搜索最近邻点。该算法利用kd树的结构,在搜索过程中通过不断划分空间,将搜索范围缩小到最小。 具体来说,kd树是一种二叉树结构,在每个节点上选择一个维度进行划分,将数据集中该维度上的值分别划分到左右子树中。通过这样的划分,kd树能够将高维空间划分为多个低维空间。kd树的构建过程可以通过递归的方式实现。 在搜索过程中,首先根据目标点的值和当前节点的划分维度比较,确定搜索的方向。然后递归地在对应的子树中进行搜索。在搜索过程中,通过判断目标点到当前最近邻点的距离与当前节点到目标点的距离的关系,决定是否需要进一步搜索其他子树。最终,找到离目标点最近的点作为最近邻点。 kd树搜索算法的时间复杂度与树的深度相关,平均情况下能够达到O(logn)的时间复杂度。该算法在处理高维空间的搜索问题上具有较好的效果,比如在图像识别、文本分类等领域被广泛应用。 总之,kd树搜索算法是一种高效的搜索算法,在处理高维空间搜索问题上具有重要的应用价值。它通过划分空间、递归搜索等策略,能够快速找到最近邻点,为解决实际问题提供了有效的解决方案。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值