高级数据结构-KD树

一、基本概念

  • KD树是一种对K维空间中的实例点进行存储以便对其进行快速检索的树形结构,表示对K维空间的一个划分
  • 用于KNN算法的实现。给定一个目标点,利用KD树可以快速地查找出距离其最近地K个点

二、构造KD树

  • 输入:k维空间的数据集T = {x1,x2…xn},其中每个xi是一个k维的向量

  • 输出:一棵KD树

  • 流程:

    • 1)构造根结点,对应于包含数据集T中所有点的超矩形区域。选择第1个维度为坐标轴,选取T中所有点第一个维度上中位数对应的点,这个点为切分点,根节点对应的区域切分成为两个子区域。同时将选取的点的坐标信息存储在这一根节点中。
    • 2)递归的利用两个子区域的点构造KD树。需要注意对于深度为j的点,选取第l个维度作为切分的坐标轴。其中l = j(mod k)+1;也就是说,例如根节点用第一维度划分,第二层的结点就用第二维度划分。如果只有两个维度的情况下,第三层的结点就回到用第一维度划分。以此类推。
    • 3)递归结束条件:两个子区域都没有点存在
  • 由算法的流程,需要设计一个ADT来表示KD树中的每一个结点。设计的ADT如下:

class KDNodes(object):#构造结点#需要保存该结点对应的点的信息#正如将在KD树搜索中看到的一样还需要保存该节点对应的划分维度#需要保存左儿子信息#需要保存右儿子信息#point信息def __init__(self,demension,point):self.demension  = demensionself.point = pointself.rc = Noneself.lc = None
  • 还需要设计一个操作的集合ADT,这个ADT集合中包含构造KD树的算法
class KDTree(object):#初始选择划分的维度为第一个维度def __init__(self,set:list):self.root = self.construct(set,1)#利用当前的点集合,构造一个KD树#parition_demension:当前用于划分的维度def construct(self,set:list,partition_demension:int):#递归终止条件if(not set):return None#选择当前维度的中位数所对应的结点作为该结点的根节点set.sort(key = lambda item:item[partition_demension-1])a = len(set)if(a % 2 == 0):index  = a//2else:index = (a-1)//2#根据中位数下标选择结点node_point = set[index]#构造根节点node = KDNodes(demension = partition_demension,point = node_point)#计算下一个划分的维度next_demension = (partition_demension)%len(set[0])+1#递归构造左子树node.lc = self.construct(set = set[:index] ,partition_demension = next_demension)#递归构造右子树node.rc = self.construct(set = set[index+1:],partition_demension=next_demension)return node

三、搜索KD树

  • 搜索KD树的目的在于给定一个目标点,用KD树快速找出与其最近的K个点
  • 输入:已经构造好的KD树,目标点x
  • 输出:x的k个最近邻
  • 算法流程:(尤其注意当前节点的变化)
    • 设 L 为一个有 k 个空位的列表,用于保存已搜寻到的最近点。
    • 1)根据 x 的坐标值和每个节点的切分向下搜索(也就是说,如果树的节点是照 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rR6liUIo-1628660441689)(https://www.zhihu.com/equation?tex=x_r%3Da)] 进行切分,并且 x 的 r 坐标小于 a,则向左枝进行搜索;反之则走右枝)。但是,如果一支没有结点,另一支有结点,还是应该走有结点的那一边。不论坐标值的大小关系,直到走到一个叶结点
    • 2)当到达一个叶结点(底部结点)时(下面叙述为当前结点):
      • 2.1)将这个叶结点标记为访问过
      • 2.2)如果L中不足k个点,则将当前结点的坐标加入L中。否则(L中已经存在K个点,即L已满)的情况下,如果当前结点与x的距离小于L中的点与目标结点x的最长距离则用当前结点替换L中的这个与x的最长距离的点;其余的情况下,不做任何操作
    • 3)如果当前结点不是整棵KD树的根节点,那么转4),否则输出列表L,算法结束。
    • 4)令当前结点当前结点的父结点。如果当前结点未被访问过,将其标记为访问过,转5);如果当前结点已经访问过,再次执行4)
    • 5)分两步进行:
      • 5.1)如果L中不足k个点,则将该当前结点加入L;否则(L中已经满了K个点),如果当前节点与目标x之间的距离小于L中与目标结点x距离最长的点对应的距离,那么用当前节点替换掉该找出的最长距离的结点。
      • 5.2)计算目标结点x与当前节点切分线(注意当时我们在树的结点的ADT中记录了切分维度,这里就派上用场了)之间的垂直距离,设该距离为distance。
        • 5.2.1)如果distance>=L中距离目标节点x最远的距离(这里设这个最远距离点为y)并且L中已经有K个点,则说明当前节点对应的切分线另一侧不会有更近的点,转3)
        • 5.2.2)否则,说明当前节点切分线另一侧可能会有更近的点,对当前结点另一支(也是一个KD树)从1)开始执行,也就是递归调用此算法。
  • 可以对构造的函数作一些修改,增加visit,用于搜索中的判断访问
    • 这里的搜索源代码优化的空间还很多。

      • 例如找最长距离不需要每次都进行计算。
      • 例如在手工栈stack中记录来源以确定另一个分支是左还是右
      • 一些重复的代码可以合并在一个函数中
class KDTree(object):def __init__(self,set:list):self.visit = {}self.root = self.construct(set,1)#利用当前的点集合,构造一个KD树def construct(self,set:list,partition_demension:int):if(not set):return None#选择当前维度的中位数所对应的结点作为该结点的根节点set.sort(key = lambda item:item[partition_demension-1])a = len(set)if(a % 2 == 0):index  = a//2else:index = (a-1)//2node_point = set[index]node = KDNodes(demension = partition_demension,point = node_point)self.visit[node] = Falsenext_demension = (partition_demension)%len(set[0])+1node.lc = self.construct(set = set[:index] ,partition_demension = next_demension)node.rc = self.construct(set = set[index+1:],partition_demension=next_demension)return node'''参数:list:现有的结点列表target:目标结点坐标返回:返回与当前目标结点距离最大的结点的目标内存与最大距离平方。如果当前list为空,那么返回None和-1;'''def search_longest(self,list,target:tuple):max = -1maxnode = -1cout = 0for node in list:distance = sum((i[0]-i[1])**2 for i in zip(node.point,target))if(distance > max):max = distancemaxnode = coutcout+=1return maxnode,max#list用于保存当前找到的结果,root表示KD树的根节点,target表示目标结点所对应的元组,k表示搜索的个数def Search(self,list:list,root:KDNodes,target:tuple,k:int):if(not root):returncurrent = rootstack = []#对应第一步while(current.lc or current.rc):  #当前结点的左右两侧有一侧不为空,往下循环,过程中将遇到的结点push到临时的栈中stack.append(current)if(not current.lc):current = current.rcelif not current.rc:current = current.lcelse:de = current.demensionif(target[de-1] < current.point[de-1]):current = current.lcelse: current = current.rcstack.append(current)#对应第二步self.visit[current] = Trueif(len(list)<k):list.append(current)else:maxnode,max = self.search_longest(list,target)if(sum((i[0]-i[1])**2 for i in zip(target,current.point)) < max):del list[maxnode]list.append(current)#该循环对应第三步while(current!=root):#对应第4步while(True):stack.pop()current  =  stack[-1]if(not self.visit[current]):self.visit[current] = Truebreak#对应5.1if (len(list) < k):list.append(current)else:maxnode, max = self.search_longest(list, target)if (sum((i[0] - i[1]) ** 2 for i in zip(target, current.point)) < max):del list[maxnode]list.append(current)# 这里的代码略显臃肿,其实可以在栈的列表中记录轨迹信息。对应5.2distance  = abs(target[current.demension-1] - current.point[current.demension-1])if(len(list) < k or distance < self.search_longest(list,target)[1]):if(current.lc and not (self.visit[current.lc])):self.Search(list,root = current.lc,target = target,k = k)elif(current.rc and not (self.visit[current.rc])):self.Search(list, root=current.rc, target=target, k=k)

四、算法分析

  • 在实例点随机分布的情况下,KD树的搜索效率是对数级别的。
  • KD树适用于训练实例数远大于空间维数的k近邻搜索
    lif(current.rc and not (self.visit[current.rc])):
    self.Search(list, root=current.rc, target=target, k=k)

## 四、算法分析- 在实例点随机分布的情况下,KD树的搜索效率是对数级别的。
- KD树适用于训练实例数远大于空间维数的k近邻搜索- 否则效率接近线性扫描

高级数据结构-KD树相关推荐

  1. 【算法学习笔记】高级数据结构 B树

    参考算法导论第18章 B树 1. B树概述 B树是为「磁盘或其他直接存取的辅助存储设备」而设计的一种平衡搜索树.B树类似于红黑树(算导第13章),但它们在降低磁盘I/O操作数 minimizing d ...

  2. 【Java版高级数据结构】树论基础二叉树

    本文涉及代码见博主的github : https://github.com/chenruoyu0319/data-structure-for-java/tree/main/%E6%A0%91%E8%A ...

  3. 高级数据结构 线段树

    线段树 #include<stdio.h> #pragma warning (disabled:4996) #include<malloc.h> //链表实现 typedef ...

  4. k-d树(Kd trees)

    前言 在学习了平衡二叉查找树.红黑树等等之后,今天我们再来学习一个新的数据结构--kd树,kd树是一种分割k维数据空间的数据结构,主要应用于多维空间关键数据的搜索,下面就让我们来详细看看这种算法. k ...

  5. python 树状数组_【算法日积月累】19-高级数据结构:树状数组

    树状数组能解决的问题 树状数组,也称作"二叉索引树"(Binary Indexed Tree)或 Fenwick 树. 它可以高效地实现如下两个操作: 1.数组前缀和的查询: 2. ...

  6. KNN的实现——KD树

         实现knn时,主要考虑的问题是如何对训练数据进行快速近邻搜索.这点在特征空间的维数大及训练数据容量大时尤其必要.       knn最简单的实现方法是线性扫描(linear scan).这时 ...

  7. 高级数据结构——红黑树

    目录 红黑树 红黑树定义 红黑树节点实现 红黑树插入实现 红黑树删除实现 红黑树 红黑树定义 在之前介绍AVL树时,我们知道AVL树是高度平衡的二叉搜索树,而高度平衡意味着在对AVL树中的节点作更新操 ...

  8. 高级数据结构实现——自顶向下伸展树

    [0]README 1) 本文部分内容转自 数据结构与算法分析,旨在理解 高级数据结构实现--自顶向下伸展树 的基础知识: 2) 源代码部分思想借鉴了数据结构与算法分析,有一点干货原创代码,for o ...

  9. 【数据结构与算法】二维Kd树的Java实现

    关于Kd树啊,可能很多人没听说过. Kd树是 K-dimension tree 的缩写,是对数据点在 K维空间(如二维(x,y),三维(x,y,z),K维(x,y,z, -))中划分的一种高维索引树形 ...

最新文章

  1. Html5 Canvas 学习之路(一)
  2. WPF入门教程(七)---依赖属性(3)(转)
  3. 计算机复试通知学校,提醒:部分学校复试通知已经出了!教育部关于21考研复试的规定!...
  4. 系统中异常公共处理模块 in spring boot
  5. 从0开始学Java——JSPServlet——HttpServletRequest相关的几个路径信息
  6. 算法当道!为什么人类和人工智能越来越像?
  7. OJ1057: 素数判定(C语言经典列题,判断变量的应用)
  8. python列表使用技巧大全_Python 基础起步 (六) List的实用技巧大全
  9. 简单 常用的git命令
  10. 10000亿的暴利:数字化营销今生与未来
  11. 在信息流上,百度或许可以给谷歌信息流支个招
  12. 安全站点导航(感谢backlion整理)
  13. Linux 中各个文件夹的作用
  14. Laravel执行seeder命令出现class *** does not exist
  15. VMware15.1安装苹果系统mac10.15.3(图解)
  16. web漏洞 云盾_云盾WAF实现虚拟补丁——记一起Web漏洞应急响应
  17. oracle游标列转行,Oracle行转列和列转行
  18. 获取Google PR值的代码!
  19. 《MySQL是怎么运行的》读书笔记
  20. C++基于OpenCV实现实时监控和运动检测记录

热门文章

  1. 人工智能研究生阶段的学习规划
  2. 将html里面的img标签拼上全路径
  3. c语言的算法一定要雪狐嘛,c语言的编程风格
  4. 2020华为杯数学建模D题交流2群
  5. 人才测评中的“七多”原则
  6. AWS 解决方案架构师考点(IAM)
  7. (三)inkscape基础 裁剪图片
  8. Linux vi命令大全
  9. 【FPGA】VGA显示文字、彩条、图片——基于DE2-115
  10. PDF怎么压缩的小一点?这两招压缩方法很实用