KdTree 最近邻查找算法(C++描述)

根据统计学习方法写的KdTree实现,###

参考了这个博客的主要思路,但是在关于如何搜索最近邻上有些不同。
1.我采取在发现可能的路径后,采取扩展路径到叶子节点,生成一个新路径后重新计算最近路径。而这个博客中只检查了路径上与超球体相交的点。没有递归搜索
2.他的博客用利用方差确定分割的方向。我则选用了简单的依次更换策略。
#include <iostream>
#include <vector>
#include <algorithm>
#include <stack>
using namespace std;
struct Node
{
double x;
double y;
};
struct KdTree
{
Node val;
int split; /描述根据X或Y进行划分/
KdTree* left;
KdTree* right;
};
KdTree myKdTree{};
const int N = 6;
const int dim = 2;
Node dataSet[N] = {
{ 2,3 },
{ 5,4 },
{ 9,6 },
{ 4,7 },
{ 8,1 },
{ 7,2 }
};
int time = 0;/记录寻找分割次数/
stack<KdTree> search_path;/记录搜索过程的路经*/

/*结果结构*/
struct result {
    Node resNode;
    double dist;
};

/*X,Y维比较函数*/
bool compareX(Node a,Node b) {
    return a.x > b.x;
}
bool compareY(Node a, Node b) {
    return a.y > b.y;
}
void chooseSplit(Node unsortSet[],Node& splitData,int size) {
    if (time % 2 == 0) {
        /*根据x维分割*/
        sort(unsortSet, unsortSet + size, compareX);
    }
    else {
        /*根据y维分割*/
        sort(unsortSet, unsortSet + size, compareY);
    }
    int mid;
    if (size % 2 == 0) {
        mid = size / 2 - 1;
    }
    else {
        mid = size / 2;
    }
    splitData.x = unsortSet[mid].x;
    splitData.y = unsortSet[mid].y;
    time++;
}

/*构造kdTree*/
KdTree* build(int size,Node unsortSet[], KdTree* tree) {
    if (size == 0) {
        return 0;
    }
    else {
        int split;
        Node splitData;
        chooseSplit(unsortSet,splitData, size);
        Node leftset[100]{};
        Node rightset[100]{}; 
        int leftnum = 0;
        int rightnum = 0;
        if (time % 2 == 1) {
            /*根据x维分割,time加一后*/
            split = 0;
            for (int i = 0; i < size; i++) {
                if (splitData.x > unsortSet[i].x) {
                    leftset[leftnum] = unsortSet[i];
                    leftnum++;
                }
                else if(splitData.x < unsortSet[i].x) {
                    rightset[rightnum] = unsortSet[i];
                    rightnum++;
                }
            }
        }
        else {
            split = 1;
            for (int i = 0; i < size; i++) {
                if (splitData.y > unsortSet[i].y) {
                    leftset[leftnum] = unsortSet[i];
                    leftnum++;
                }
                else if (splitData.y < unsortSet[i].y) {
                    rightset[rightnum] = unsortSet[i];
                    rightnum++;
                }
            }
        }
        tree = new KdTree;
        tree->val = splitData;
        tree->split = split;
        tree->left = build(leftnum, leftset, tree->left);
        tree->right = build(rightnum, rightset, tree->right);
        return tree;

    }
}

/*计算距离 p=2*/
double distance(Node a, Node b) {
    return (a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y);
}

/*建立搜索路径*/
void buildpath(Node target, KdTree* tree) {
    KdTree* pSearch = tree;
    while (pSearch != NULL) {
        search_path.push(pSearch);
        if (pSearch->split == 0) {
            if (target.x < pSearch->val.x) {
                pSearch = pSearch->left;
            }
            else {
                pSearch = pSearch->right;
            }
        }
        else {
            if (target.y < tree->val.y) {
                pSearch = pSearch->left;
            }
            else {
                pSearch = pSearch->right;
            }
        }
    }
}

/*根据搜索路径查找最近邻*/
result findnearest (Node target,KdTree* tree){
    /*初始化搜索路径*/
    buildpath(target, tree);
    Node nearest = search_path.top()->val;
    double dist = distance(nearest, target);
    search_path.pop();
    //搜索潜在的路径上最近点。
    KdTree* pBack;
    while (search_path.size() != 0) {
        pBack = search_path.top();
        search_path.pop();
        if (pBack->left == NULL && pBack->right == NULL) {
            if (distance(pBack->val, target) < dist) {
                dist = distance(pBack->val, target);
                nearest = pBack->val;
            }
        }
        else {
            if (pBack->split == 0) {
                if (abs(target.x - pBack->val.x) < dist) {//X方向相交。
                    KdTree* newTree{};
                    if ((target.x > pBack->val.x)&&(pBack->left !=NULL)) {//点在右侧,向左搜索。
                        search_path.push(pBack->left);
                        newTree = pBack->left;
                    }
                    if ((target.x < pBack->val.x) && (pBack->right != NULL)) {
                        search_path.push(pBack->right);
                        newTree = pBack->right;
                    };
                    //搜索新发现的路径
                    buildpath(target, newTree);
                }
            }
            else {
                if (abs(target.y - pBack->val.y) < dist) {//Y方向相交。
                    KdTree* newTree{};
                    if ((target.y > pBack->val.y) && (pBack->left != NULL)) {//点在右侧,向左搜索。
                        search_path.push(pBack->left);
                        newTree = pBack->left;
                    }
                    if ((target.y < pBack->val.y) && (pBack->right != NULL)) {
                        search_path.push(pBack->right);
                        newTree = pBack->right;
                    };
                    //搜索新发现的路径
                    buildpath(target, newTree);
                }
            }
        }

    }
    return result{ nearest ,dist };
}
    
    

//打印树结构
void printNode(Node node) {
    cout << "("<<node.x<<","<<node.y<<")"<<endl;
}

void printTree_rootfirst(KdTree* root) {
    printNode(root->val);
    if (root->left != NULL) {
        printTree_rootfirst(root->left);
    }
    if (root->right != NULL) {
        printTree_rootfirst(root->right);
    }
}

void printTree_leftfirst(KdTree* root) {
    if (root->left != NULL) {
        printTree_leftfirst(root->left);
    }
    printNode(root->val);
    if (root->right != NULL) {
        printTree_leftfirst(root->right);
    }
}
int main() {
    KdTree * root = NULL;
    root = build(N, dataSet, root); 
    Node target = {2,4.5};
    result res = findnearest(target,root);
    cout <<"最近距离:"<< res.dist << endl;
    cout <<"X方向:"<< res.resNode.x << endl;
    cout << "Y方向:" << res.resNode.y << endl;
    system("pause");
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,099评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,828评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,540评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,848评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,971评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,132评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,193评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,934评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,376评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,687评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,846评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,537评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,175评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,887评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,134评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,674评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,741评论 2 351

推荐阅读更多精彩内容