首页 > 其他分享 >k-D Tree小记

k-D Tree小记

时间:2023-10-31 22:14:58浏览次数:24  
标签:复杂度 Tree 中位数 维度 Theta 长方体 小记

k-D Tree 是一种能够 高效处理 \(k\) 维空间信息 的数据结构。

建树

k-D Tree 具有二叉搜索树的形态,二叉搜索树上的每个结点都对应 \(k\) 维空间内的一个点。其每个子树中的点都在一个 \(k\) 维的超长方体内,这个超长方体内的所有点也都在这个子树中。

假设我们已经知道了 \(k\) 维空间内的 \(n\) 个不同的点的坐标,要将其构建成一棵 k-D Tree,步骤如下:

  • 若当前超长方体中只有一个点,返回这个点。
  • 选择一个维度,将当前超长方体按照这个维度分成两个超长方体。
  • 选择切割点:在选择的维度上选择一个点,这一维度上的值小于这个点的归入一个超长方体(左子树),其余的归入另一个超长方体(右子树)。
  • 将选择的点作为这棵子树的根节点,递归对分出的两个超长方体构建左右子树,维护子树的信息。

为了方便理解,我们举一个 \(k=2\) 时的例子。

其构建出 k-D Tree 的形态可能是这样的:

其中树上每个结点上的坐标是选择的分割点的坐标,非叶子结点旁的 x 或 y 是选择的切割维度。

这样的复杂度无法保证。对于 \(2,3\) 两步,我们提出两个优化:

  • 轮流选择 \(k\) 个维度,以保证在任意连续 \(k\) 层里每个维度都被切割到。
  • 每次在维度上选择切割点时选择该维度上的 中位数,这样可以保证每次分成的左右子树大小尽量相等。
    可以发现,使用优化 \(2\) 后,构建出的 k-D Tree 的树高最多为 \(\log n+\Theta(1)\)。

现在,构建 k-D Tree 时间复杂度的瓶颈在于快速选出一个维度上的中位数,并将在该维度上的值小于该中位数的置于中位数的左边,其余置于右边。如果每次都使用 sort 函数对该维度进行排序,时间复杂度是 \(\Theta(n\log^2 n)\) 的。事实上,单次找出 n 个元素中的中位数并将中位数置于排序后正确的位置的复杂度可以达到 \(\Theta(n)\)。

我们来回顾一下快速排序的思想。每次我们选出一个数,将小于该数的置于该数的左边,大于该数的置于该数的右边,保证该数在排好序后正确的位置上,然后递归排序左侧和右侧的值。这样的期望复杂度是 \(\Theta(n\log n)\) 的。但是由于 k-D Tree 只要求要中位数在排序后正确的位置上,所以我们只需要递归排序包含中位数的 一侧。可以证明,这样的期望复杂度是 \(\Theta(n)\) 的。在 algorithm 库中,有一个实现相同功能的函数 nth_element(),要找到 s[l]s[r] 之间的值按照排序规则 cmp 排序后在 s[mid] 位置上的值,并保证 s[mid] 左边的值小于 s[mid],右边的值大于 s[mid],只需写 nth_element(s+l,s+mid,s+r+1,cmp)

借助这种思想,构建 k-D Tree 时间复杂度是 \(\Theta(n\log n)\) 的。

高维空间上的操作

在查询高维矩形区域内的所有点的一些信息时,记录每个结点子树内每一维度上的坐标的最大值和最小值。如果当前子树对应的矩形与所求矩形没有交点,则不继续搜索其子树;如果当前子树对应的矩形完全包含在所求矩形内,返回当前子树内所有点的权值和;否则,判断当前点是否在所求矩形内,更新答案并递归在左右子树中查找答案。

复杂度为 \(T(n)=\Theta(n^{\frac{k-1}{k}})\) (\(k\) 视为常数)。

插入/删除

如果维护的这个 \(k\) 维点集是可变的,即可能会插入或删除一些点,此时 k-D Tree 的平衡性无法保证。由于 k-D Tree 的构造,不能支持旋转,类似与 FHQ Treap 的随机优先级也不能保证其复杂度。对此,有两种比较常见的维护方法,我一般用替罪羊树的重构方法。

标签:复杂度,Tree,中位数,维度,Theta,长方体,小记
From: https://www.cnblogs.com/code-ac/p/17801690.html

相关文章

  • 「Log」2023.10.30 小记
    序幕\(\text{6:50}\):昏暗到校,写CF杂题。经过两个小时的思考终于看懂了题解。\(\color{blueviolet}{CF1530F}\)此题是神秘题。考虑反着做,将至少有一行或一列或一条对角线全为\(1\)概率转换为所有行列对角线都至少有一个\(0\)。先不考虑行与对角线,只考虑满足所有列都至少......
  • Sourcetree 合并分支到主干
     现在要合并dev分支到prd分支1、首先切换到dev分支,然后拉取代码,然后切换到prd分支,拉取代码;2、然后右击dev分支3、选择合并dev分支至当前分支(当前分支为prd分支)4、然后prd分支会显示有多个文件待推送,推送到仓库即可......
  • QTreeWidget 的搜索实时显示功能
    QTreeWidget的子条目很多时候需要提供实时的搜索功能,以便能快速找到所需要的条目。代码如下://1.创建当输入框文本变化时的信号槽。connect(ui.lineEditSearch,&QLineEdit::textChanged,this,&Demo01_GUI::OnFindItem);//2.槽函数实现检索时,实时显示符合要求的QTre......
  • Element Plus el-tree懒加载默认选中
    百度上试了很多方法,设置default-expanded-keys不生效,最后使用了下面的方法,亲测有效constloadNode=async(node:Node,resolve:(data:AreaType[])=>void)=>{if(node.level===0){const{data}=awaitgetRegionList(areaOptions)if(!props.special)......
  • QTreeWidget 添加右键菜单
    有时需要为QTreeWidget的子条目添加右键菜单功能,主要有两种方案来实现:方案一该方案比较通用,通过为QTreeWidget建立信号槽,在接受itemPressed的信号时会被触发,然后判断当前是否为鼠标右键,若为鼠标右键则创建添加对应的菜单栏,并提供相应的功能。//1.QTreeWidget*tree为......
  • PAT甲级:1174 Left-View of Binary Tree
     题目:1174Left-ViewofBinaryTree25分 题解:层次遍历输出每一行最左边的元素。(最开始以为输出部分节点的左子树...想不到思路)usingnamespacestd;#include<iostream>#include<vector>#include<map>#include<algorithm>#include<queue>#include<cmath>......
  • 10.28 模拟赛小记
    梦熊10连测的第八个了。比赛地址写在亲前面的总结:因为下午班级合唱比赛,所以不太想打比赛,想去看演出的。鉴于我们第一个唱完,以及班主任说节目可以看到15:40,所以一直在玩上去的很晚。之后在机房继续看完了节目。所以本场打的还挺抽象。更加难评的是这竟然是我打的最好的一场(?),有......
  • CF762F Tree nesting
    来一点更清楚的、实现方面的东西。做法同这篇,他的实现很优美但略微繁琐了些。枚举\(T\)的形态,发现这个匹配不过是把每个\(T\)中当前点的儿子塞进一个\(S\)中当前点的儿子内。于是\(f_{u,v}\)表示\(S\)中\(u\)匹配\(T\)中\(v\)且\(v\)整棵子树被匹配完的方案......
  • CF375E Red and Black Tree
    看错题看成只能交换相邻节点颜色了/fn每次操作交换两个节点颜色,可以转化为统计最终合法颜色序列相比开始,最少有多少个红点变成黑点。可以考虑一个类似树形dp的过程,对于每个节点我们钦定下它会被哪个节点“笼罩”,同时由于黑点数量有限,我们还需要记录下子树内已经用了多少个黑点......
  • 「Log」2023.10.27 小记
    序幕\(\text{6:50}\):到校,早上稍微墨迹了一小会。一直不会的某个结论差不多会证明了,先写一下题再写写题解。\(\color{blueviolet}{CF1495D}\)此题是好题。考虑对于\(x\)和\(y\)共同的生成树一定包含两者的最短路径。先假设\(x,y\)最短路径有且只有一条,考虑其上一点\(......