K-D tree入门【更新ing】

久仰K-D tree大名已久,终于在合适的时候遇见了合适的水题入了坑入了门

K-D tree是什么

K-D tree是什么? 按名字上翻译来就是K维的树,就是一个用来维护K维空间的点的平衡二叉树

K-D tree有什么用

K-D tree可以进行空间上的操作,最经典的就是查询最近/最远 点对

还有很多我不知道

K-D tree的原理与实现

K-D tree,又有一个名字叫做划分树,与其原理相联系

类似于普通的平衡树,对于普通的平衡树的节点u,其左右子树分别是权值小于u的和权值大于u的,该节点就相当于在一个值域上在u的值处进行了分割

而kd-tree对于多维空间进行分割,一个节点储存着以下信息:

struct node{
    int d[K],s[2],x[2],y[2],......;
}e[maxn];

d就是该节点储存的点的K维坐标

s储存着其左右儿子

剩余的若干数组储存着以该节点为根的子树的各维度的最值

也就是说,一棵子树实际上对应着一个空间区域,而根节点将该空间区域划分为左右两部分

而该空间的信息就储存在子树的根节点中

但是这是在一个多维空间,将空间区域划分有多种方式

一般地,kd-tree垂直于其中一个坐标轴将平面划分开

以2维为例,如下图所示

如图圆圈表示节点,将区域进行划分

一般遵循以下规律构造:

①各层节点交替划分各维空间

根节点划分x坐标

其儿子划分y坐标

其孙子划分z坐标

......

②每一层的区域中,按该层划分的坐标排序,选取其中位数作为划分点进行划分

切点作为父节点,左边的点划分到左子树中,右边的点划分到右子树中

③逐层划分,直至划分区域无节点

在C++的STL中,有一个函数nth_element()可以在\(O(n)\)时间内将一个数组第k大找出,并将小于的放在左边,大于的放在右边

具体实现类似快排

对于K维空间,建树复杂度\(O(Knlogn)\)

二维建树代码如下:

#define ls e[u].s[0]
#define rs e[u].s[1]
#define cmin(x,y) (x > y ? x = y : x)
#define cmax(x,y) (x < y ? x = y : x)
struct point{int d[2];}a[maxn];
struct node{int d[2],s[2],x[2],y[2];}e[maxn];
int n,rt,D,x,y;
bool operator <(const point& a,const point& b){
    return a.d[D] == b.d[D] ? a.d[D ^ 1] < b.d[D ^ 1] : a.d[D] < b.d[D];
}
void pup(int u){
    if (ls){
        cmin(e[u].x[0],e[ls].x[0]); cmax(e[u].x[1],e[ls].x[1]);
        cmin(e[u].y[0],e[ls].y[0]); cmax(e[u].y[1],e[ls].y[1]);
    }
    if (rs){
        cmin(e[u].x[0],e[rs].x[0]); cmax(e[u].x[1],e[rs].x[1]);
        cmin(e[u].y[0],e[rs].y[0]); cmax(e[u].y[1],e[rs].y[1]);
    }
}
int build(int l,int r,int d){
    D = d; int u = l + r >> 1;
    nth_element(a + l,a + u,a + r + 1);
    e[u].d[0] = e[u].x[0] = e[u].x[1] = a[u].d[0];
    e[u].d[1] = e[u].y[0] = e[u].y[1] = a[u].d[1];
    if (l < u) ls =  build(l,u - 1,d ^ 1);
    if (r > u) rs =  build(u + 1,r,d ^ 1);
    pup(u);
    return u;
}

查询最近/远点对

以最近为例

与普通的暴力不同,在KDtree中查询最近点对,预期复杂度为\(O(logn)\),可以被卡为\(O(\sqrt{N})\)

我们到一个节点时,用该节点更新答案,并计算左右子树的估价函数

由于每棵子树都对应一个区域,可以由此计算出每棵子树的最近值

如果最近的点的贡献都比当前答案大,那么就不用访问该子树了

以2维为例,估价函数可以这样写:

#define getd(u) (max(x - e[u].x[1],0) + max(e[u].x[0] - x,0) + max(y - e[u].y[1],0) + max(e[u].y[0] - y,0))
#define getdx(u) (max(abs(e[u].x[0] - x),abs(e[u].x[1] - x)) + max(abs(e[u].y[0] - y),abs(e[u].y[1] - y)))

实际上就是求与四个顶点距离的最值

由此可以写出搜索函数:

void qmx(int u){
    LL t = equal(u) ? -INF : (abs(e[u].d[0] - x) + abs(e[u].d[1] - y)),d[2];
    if (ls) d[0] = getdx(ls); else d[0] = -INF;
    if (rs) d[1] = getdx(rs); else d[1] = -INF;
    cmax(mx,t); t = d[0] <= d[1];
    if (d[t] > mx) qmx(e[u].s[t]); t ^= 1;
    if (d[t] > mx) qmx(e[u].s[t]);
}
void qmn(int u){
    int t = equal(u) ? INF : (abs(e[u].d[0] - x) + abs(e[u].d[1] - y)),d[2];
    if (ls) d[0] = getd(ls); else d[0] = INF;
    if (rs) d[1] = getd(rs); else d[1] = INF;
    cmin(mn,t); t = d[0] >= d[1];
    if (d[t] < mn) qmn(e[u].s[t]); t ^= 1;
    if (d[t] < mn) qmn(e[u].s[t]);
}

例题

由以上基础,我们就可以轻松A掉SDOI2010 hideseek

用每个点搜一次就好

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long int
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<‘ ‘; puts("");
#define ls e[u].s[0]
#define rs e[u].s[1]
#define cmin(x,y) (x > y ? x = y : x)
#define cmax(x,y) (x < y ? x = y : x)
#define getd(u) (max(x - e[u].x[1],0) + max(e[u].x[0] - x,0) + max(y - e[u].y[1],0) + max(e[u].y[0] - y,0))
#define getdx(u) (max(abs(e[u].x[0] - x),abs(e[u].x[1] - x)) + max(abs(e[u].y[0] - y),abs(e[u].y[1] - y)))
#define equal(u) (e[u].d[0] == x && e[u].d[1] == y)
using namespace std;
const int maxn = 100005,maxm = 100005,INF = 2100000000;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();}
    while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    return out * flag;
}
struct point{int d[2];}a[maxn];
struct node{int d[2],s[2],x[2],y[2];}e[maxn];
int n,rt,D,x,y; LL mx,mn;
bool operator <(const point& a,const point& b){
    return a.d[D] == b.d[D] ? a.d[D ^ 1] < b.d[D ^ 1] : a.d[D] < b.d[D];
}
void pup(int u){
    if (ls){
        cmin(e[u].x[0],e[ls].x[0]); cmax(e[u].x[1],e[ls].x[1]);
        cmin(e[u].y[0],e[ls].y[0]); cmax(e[u].y[1],e[ls].y[1]);
    }
    if (rs){
        cmin(e[u].x[0],e[rs].x[0]); cmax(e[u].x[1],e[rs].x[1]);
        cmin(e[u].y[0],e[rs].y[0]); cmax(e[u].y[1],e[rs].y[1]);
    }
}
int build(int l,int r,int d){
    D = d; int u = l + r >> 1;
    nth_element(a + l,a + u,a + r + 1);
    e[u].d[0] = e[u].x[0] = e[u].x[1] = a[u].d[0];
    e[u].d[1] = e[u].y[0] = e[u].y[1] = a[u].d[1];
    if (l < u) ls =  build(l,u - 1,d ^ 1);
    if (r > u) rs =  build(u + 1,r,d ^ 1);
    pup(u);
    return u;
}
void qmx(int u){
    LL t = equal(u) ? -INF : (abs(e[u].d[0] - x) + abs(e[u].d[1] - y)),d[2];
    if (ls) d[0] = getdx(ls); else d[0] = -INF;
    if (rs) d[1] = getdx(rs); else d[1] = -INF;
    cmax(mx,t); t = d[0] <= d[1];
    if (d[t] > mx) qmx(e[u].s[t]); t ^= 1;
    if (d[t] > mx) qmx(e[u].s[t]);
}
void qmn(int u){
    int t = equal(u) ? INF : (abs(e[u].d[0] - x) + abs(e[u].d[1] - y)),d[2];
    if (ls) d[0] = getd(ls); else d[0] = INF;
    if (rs) d[1] = getd(rs); else d[1] = INF;
    cmin(mn,t); t = d[0] >= d[1];
    if (d[t] < mn) qmn(e[u].s[t]); t ^= 1;
    if (d[t] < mn) qmn(e[u].s[t]);
}
int main(){
    n = read();
    for (int i = 1; i <= n; i++) a[i].d[0] = read(),a[i].d[1] = read();
    rt = build(1,n,0);
    LL ans = INF;
    for (int i = 1; i <= n; i++){
        x = a[i].d[0]; y = a[i].d[1];
        mx = 0; qmx(rt);
        mn = INF; qmn(rt);
        ans = min(ans,mx - mn);
    }
    printf("%lld\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/Mychael/p/8569539.html

时间: 2024-10-18 08:18:03

K-D tree入门【更新ing】的相关文章

若干数据结构 && 算法面试题【四】(更新ing)

这是我的第三个面试题汇总. 想看之前的内容,请移步: http://zhweizhi.blog.51cto.com/10800691/1763237 ( 若干数据结构 && 算法面试题[一](更新完毕)) http://zhweizhi.blog.51cto.com/10800691/1775780 ( 若干数据结构 && 算法面试题[二](更新完毕)) http://zhweizhi.blog.51cto.com/10800691/1787562 ( 若干数据结构 &am

bzoj2049 [Sdoi2008]Cave 洞穴勘测 link cut tree入门

link cut tree入门题 首先说明本人只会写自底向上的数组版(都说了不写指针.不写自顶向下QAQ……) 突然发现link cut tree不难写... 说一下各个函数作用: bool isroot(int x):判断x是否为所在重链(splay)的根 void down(int x):下放各种标记 void rotate(int x):在x所在重链(splay)中将x旋转到fa[x]的位置上 void splay(int x):在x坐在重链(splay)中将x旋转到根 void acce

论深度优先(DFS)和广度优先搜索(BF)的优点及不足(更新ing)

例题: POJ 1915 Knight Moves 骑士遍历问题(跳马问题) 在一个m*m的棋盘上,从任意一个给定的位置(sx , sy)出发,为象棋中的马找一条路通过最少的步数到达另一位置(ex ,ey),输出最少所需要的步数. 利用bfs求解. 当马在位置(x , y)的时候其后继节点(后继选择)是什么? 对于马,有八个方向可以选择,马可以跳到如下几个位置: (x+2 , y+1) , (x+1 , y+2 ) , (x-1 , y+2) , (x-2 , y+1), (x+2 , y -1

storcli 命令(更新Ing)

help 1 [[email protected]]# storcli -h 2 Storage Command Line Tool Ver 007.0606.0000.0000 Mar 20, 2018 3 4 (c)Copyright 2018, AVAGO Technologies, All Rights Reserved. 5 6 7 storcli -v 8 storcli -h| -help| ? 9 storcli -h| -help| ? legacy 10 storcli sh

我的模板(持续更新ing)[还很混乱]

1.头文件 短的: 1 #include<bits/stdc++.h> 2 #define cl(a,b) memset(a,b,sizeof(a)) 3 #define debug(a) cerr<<#a<<"=="<<a<<endl 4 using namespace std; 5 typedef long long ll; 6 typedef pair<int,int> pii; 7 8 const int

线段树入门(更新单个节点)

很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少. 这让很多学生很反感. 不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问.当然,老师有时候需要更新某位同学的成绩. Input本题目包含多组测试,请处理到文件结束. 在每个测试的第一行,有两个正整数 N 和 M ( 0<N<=200000,0<M<5000 ),分别代表学生的数目和操作的数目. 学生ID编号分别从1编到N. 第二行包含N个整数,代表这N个学生的初始成绩,

来吧,IT小小鸟(持续更新ing)

我是一只小小小小鸟,想要飞呀飞呀却飞不高啊....... 有人冷冷应对:你飞不高是因为学的不够 [中国目前还是启蒙时代,路子一不留神就错了.我更建议通过大量阅读,从科学中获得方法:技术与创业获得财富:大时间周期进行自我监控.自我反思与自我练习,获得竞争优势:参与志同道合的社区,与自我驱动的人共事,获得友情的路子. 未来时代,商业合作与个人发展,日益"阳谋".可以持续性地利用信息不对称牟利的机会越来越少:反之,可以持续性地利用智力不对称谋利的机会越来越多.当然,我所定义的智力,是神经智力

Python书籍推荐(更新ing...)

学习一门语言最怕的就是没有目标,在出发之前心里必须明确你想要做什么,这是我这个还没入门的新人给初学者的忠告. 推荐几本初学者Python教程: 2018-8-22更新 新增tkinter编写GUI界面:<Python GUI Programming Cookbook><2014年度辛星Tkinter教程第二版> 新增计划:<Python For Data Analysis><2014年辛星python标准库整理第一版> 2018-6-29更新 已看完的有:&

FireFox所支持的全部标签(持续更新ing)

近期研究上各个浏览器的差别,得到一些资料,FireFox眼下所支持的全部标签类型,持续更新,供大家參考和学习,不喜勿喷哦 http://mxr.mozilla.org/seamonkey/source/parser/htmlparser/src/nsElementTable.cpp