kd树在KNN中的应用

这 是 一 个 伪 代 码!!!

写这篇文章的目的在于理解kd树在KNN算法中的应用, 弄清楚整个搜索和回溯过程

首先, 定义kd树结点的结构体

#include <stdio.h>

typedef struct KD_Node
{
	int kindex;   //关键点直方图方差最大向量系列位置
	int kvalue;//直方图方差最大向量系列中最中间模值  
	int n;  //特征向量的维度
	int leaf; //是否是叶子结点, 是则为1
	struct KD_Node* left;  //左孩子结点
	struct KD_Node* right;  //右孩子结点
	struct Feature_Node* data;   //特征向量
}KD_Node;
然后, 定义一个函数, 这个函数的输入是一个kd树根节点和target(即待分类的样本)

输出是target在搜索过程中遇到的叶子结点

KD_Node* bitSearch(KD_Node kdtree, elemtype target, int k)

{
	//进行二叉搜索, 找到target所在区域的叶子结点, k为维度
	KD_Node* head = kdtree;
	int split_dim = kdtree->kindex;  //初始的split维度
	while(head->leaf == 0)  //只要不是叶子结点
	{
		if(target[split_dim]<=head->data[split_dim])
		{
			head = head->left;
		}
		else
		{
			head = head->right;
		}
	}//此时head变成了叶子结点
	min_dist = Dist(head, target);//求此时叶子结点与target之间的距离,并作为最小距离
	return head;
}

接着, 就是KNN的伪代码啦

我们 首先需要将target和kd树的根节点让进二叉搜索的函数, 扔完获得一个堆栈stack, 它保存了在搜索过程(search path)中遇到的每个结点, 同时, 一旦这次搜索过程结束, 我们就可以得到target所属小区域的叶子结点了, 计算与它的距离作为当前最小距离, 并把这个叶子结点初始化为最近邻点

然后; 我们需要从堆栈中一一弹出之前遇到的每个结点, 对于每个结点M:

    1.都要计算target与这个结点M所在超平面的距离(在split维度上的坐标差值), 如果距离小于当前最小距离, 则证明在这个超平面的另一侧很可能存在最近邻点. 那我们就得去搜一搜!!也就是得把另一侧的结点和target一起扔进二叉搜索函数找找离target最近的叶子结点. 那么, 如果target在这个超平面的左侧(也就是这个结点的左分支), 我们就要对M的右子树进行搜索, 反之则对左子树搜索. 

    2. 搜索完事, 如果计算的距离小于当前最小距离, 则更新最小距离和最近邻点

void KNN(KD_Node kdtree, elemtype target)

{
	//进行二叉搜索, 找到target所在区域的叶子结点
	STACK stack;
	KD_Node* head = kdtree;
	int split_dim = kdtree->kindex;  //初始的split维度
	stack.push(head);
	while(head->leaf == 0)  //只要不是叶子结点
	{
		if(target[split_dim]<=head->data[split_dim])
		{
			head = head->left;
			stack.push(head);
		}
		else
		{
			head = head->right;
			stack.push(head);
		}
	}//此时head变成了叶子结点
	stack.push(head);

	int min_dist = Dist(head, target);//求此时叶子结点与target之间的距离,并作为最小距离
	KD_Node* MIN_node = head;  //初始化此时的最近邻结点为叶子结点
	//接下来该从叶子结点逆向回溯
	while(!stack.Isempty)  //只要stack不为空
	{
		node = stack.pop();  //弹出一个最近搜索过的元素
		int split_dim = node->kindex;
		if(Dist(node->data[split_dim], target[split_dim]) < min_dist)
			//证明在当前结点的子区域内存在比当前结点更近的点
		{
			if(!node->leaf)//保证了当前结点不是叶子结点, 也就保证了它一定有子区域
			{
				//如果target在当前结点的左边, 则从右边寻找
				if(target[split_dim] < node->data[split_dim])
					node = node->right;
				else
					node = node->left;
			}

			KD_Node* min_node = bitSearch(node, target);
			dist = Dist(min_node, target);//求此时叶子结点与target之间的距离,并作为最小距离
			if(min_dist > dist)
			{
				min_dist = dist;
				MIN_node = min_node;
			}
		}

	}
}

拿统计学习方法44页的例题为例:


在第一次二叉搜索过程中, stack中保存的结点依次是 :A->B->D

然后pop D, 
计算target结点s到D结点所在平面的距离, 小于当前最小距离, 但是由于D是叶子结点, 因此不存在子节点, 当然在D的另一个子区域内就不存在距离更近的点了

接下来 pop B
计算结点s与B所在平面的距离, 发现大于当前最小距离

接下来pop A
计算结点s与A所在平面的距离, 小于当前最小距离, 因此, 在A的另一侧可能存在距离target更近的点
若target在A的左侧(即左分支), 则应在A的右侧(右分支)找, 为什么呢? 因为A的左侧都已经回溯完了, 就该右侧啦
即此时node变为结点C; 也就是说以C为根节点的分支中可能存在最近邻点. 
那么就要从C结点出发, 依次与target作比较. 一直搜索到叶子结点E, 并将结点E与target s计算距离, 若小于当前最小距离, 则更新最近邻点和最近距离


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值