kd tree学习笔记 (最近邻域查询)
https://zhuanlan.zhihu.com/p/22557068
http://blog.csdn.net/zhjchengfeng5/article/details/7855241
KD树在算法竞赛中主要用来做各种各样的平面区域查询,包含则累加直接返回,相交则继续递归,相离的没有任何贡献也直接返回。可以处理圆,三角形,矩形等判断起来相对容易的平面区域内的符合加法性质的操作。
比如查询平面内欧几里得距离最近的点的距离。
kdtree其实有点像搜索,暴力+剪枝。
每次从根结点向下搜索,并进行剪枝操作,判断是否有必要继续搜索。
它是通过横一刀,竖一刀,横一刀再竖一刀将平面进行分割,建立二叉树。
建树的复杂度是O(nlogn), 每次用nth_element()在线性时间内取出中位数。 T(n) = 2T(n/2) + O(n) = O(nlogn)
查询复杂度呢? 据第二个链接的博客说最坏是O( sqrt(n) ) 的。并不会分析查询复杂度。
HDU2966 裸kdtree
题意:给平面图上N(1 ≤ N ≤100000)个点,对每个点,找到其他 欧几里德距离 离他最近的点,输出他们之间的距离。保证没有重点。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
#define N 200010
const ll inf = 1e18;
int n,i,id[N],root,cmp_d;
int x, y;
struct node{int d[],l,r,Max[],Min[],val,sum,f;}t[N];
bool cmp(const node&a,const node&b){return a.d[cmp_d]<b.d[cmp_d];}
void umax(int&a,int b){if(a<b)a=b;}
void umin(int&a,int b){if(a>b)a=b;}
void up(int x){
if(t[x].l){
umax(t[x].Max[],t[t[x].l].Max[]);
umin(t[x].Min[],t[t[x].l].Min[]);
umax(t[x].Max[],t[t[x].l].Max[]);
umin(t[x].Min[],t[t[x].l].Min[]);
}
if(t[x].r){
umax(t[x].Max[],t[t[x].r].Max[]);
umin(t[x].Min[],t[t[x].r].Min[]);
umax(t[x].Max[],t[t[x].r].Max[]);
umin(t[x].Min[],t[t[x].r].Min[]);
}
}
int build(int l,int r,int D,int f){
int mid=(l+r)>>;
cmp_d=D,std::nth_element(t+l,t+mid,t+r+,cmp);
id[t[mid].f]=mid;
t[mid].f=f;
t[mid].Max[]=t[mid].Min[]=t[mid].d[];
t[mid].Max[]=t[mid].Min[]=t[mid].d[];
//t[mid].val=t[mid].sum=0;
if(l!=mid)t[mid].l=build(l,mid-,!D,mid);else t[mid].l=;
if(r!=mid)t[mid].r=build(mid+,r,!D,mid);else t[mid].r=;
return up(mid),mid;
} ll dis(ll x1, ll y1, ll x, ll y) {
ll xx = x1-x, yy = y1-y;
return xx*xx+yy*yy;
}
ll dis(int p, ll x, ll y){//估价函数, 以p为子树的最小距离
ll xx = , yy = ;
if(t[p].Max[] < x) xx = x-t[p].Max[];
if(t[p].Min[] > x) xx = t[p].Min[]-x;
if(t[p].Max[] < y) yy = y-t[p].Max[];
if(t[p].Min[] > y) yy = t[p].Min[]-y;
return xx*xx+yy*yy;
}
ll ans;
void query(int p){
ll dl = inf, dr = inf, d = dis(t[p].d[], t[p].d[], x, y);
if(d) ans = min(ans, d); if(t[p].l) dl = dis(t[p].l, x, y);
if(t[p].r) dr = dis(t[p].r, x, y);
if(dl < dr){
if(dl < ans) query(t[p].l);
if(dr < ans) query(t[p].r);
}
else {
if(dr < ans) query(t[p].r);
if(dl < ans) query(t[p].l);
}
} int main(){
int T; scanf("%d", &T);
while(T--){
scanf("%d", &n);
for(int i = ; i <= n; i++){
scanf("%d%d", &t[i].d[], &t[i].d[]);
t[i].f = i;
}
int rt = build(, n, , );
for(int i = ; i <= n; i++){
ans = inf;
x = t[ id[i] ].d[], y = t[ id[i] ].d[];
query(rt);
printf("%lld\n", ans);
}
}
return ;
}
BZOJ2648
题意:给出n个点,接下来m个操作,每次插入一个点,或者询问离询问点的最近曼哈顿距离。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
#define N 1000010
const ll inf = 1e18;
int n,m,i,id[N],root,cmp_d,rt;
int x, y;
struct node{int d[],l,r,Max[],Min[],val,sum,f;}t[N];
bool cmp(const node&a,const node&b){return a.d[cmp_d]<b.d[cmp_d];}
void umax(int&a,int b){if(a<b)a=b;}
void umin(int&a,int b){if(a>b)a=b;}
void up(int x){
if(t[x].l){
umax(t[x].Max[],t[t[x].l].Max[]);
umin(t[x].Min[],t[t[x].l].Min[]);
umax(t[x].Max[],t[t[x].l].Max[]);
umin(t[x].Min[],t[t[x].l].Min[]);
}
if(t[x].r){
umax(t[x].Max[],t[t[x].r].Max[]);
umin(t[x].Min[],t[t[x].r].Min[]);
umax(t[x].Max[],t[t[x].r].Max[]);
umin(t[x].Min[],t[t[x].r].Min[]);
}
}
int build(int l,int r,int D,int f){
int mid=(l+r)>>;
cmp_d=D,std::nth_element(t+l,t+mid,t+r+,cmp);
id[t[mid].f]=mid;
t[mid].f=f;
t[mid].Max[]=t[mid].Min[]=t[mid].d[];
t[mid].Max[]=t[mid].Min[]=t[mid].d[];
//t[mid].val=t[mid].sum=0;
if(l!=mid)t[mid].l=build(l,mid-,!D,mid);else t[mid].l=;
if(r!=mid)t[mid].r=build(mid+,r,!D,mid);else t[mid].r=;
return up(mid),mid;
} ll dis(ll x1, ll y1, ll x, ll y) {
return abs(x1-x)+abs(y1-y);
//ll xx = x1-x, yy = y1-y;
//return xx*xx+yy*yy;
}
ll dis(int p, ll x, ll y){//估价函数, 以p为子树的最小距离
ll xx = , yy = ;
if(t[p].Max[] < x) xx = x-t[p].Max[];
if(t[p].Min[] > x) xx = t[p].Min[]-x;
if(t[p].Max[] < y) yy = y-t[p].Max[];
if(t[p].Min[] > y) yy = t[p].Min[]-y;
return xx+yy;
//return xx*xx+yy*yy;
}
ll ans;
void ins(int now, int k, int x){
if(t[x].d[k] >= t[now].d[k]){
if(t[now].r) ins(t[now].r, !k, x);
else
t[now].r = x, t[x].f = now;
}
else {
if(t[now].l) ins(t[now].l, !k, x);
else t[now].l = x, t[x].f = now;
}
up(now);
}
void query(int p){
ll dl = inf, dr = inf, d = dis(t[p].d[], t[p].d[], x, y);
ans = min(ans, d); if(t[p].l) dl = dis(t[p].l, x, y);
if(t[p].r) dr = dis(t[p].r, x, y);
if(dl < dr){
if(dl < ans) query(t[p].l);
if(dr < ans) query(t[p].r);
}
else {
if(dr < ans) query(t[p].r);
if(dl < ans) query(t[p].l);
}
} int main(){
scanf("%d%d", &n, &m);
for(int i = ; i <= n; i++)
scanf("%d%d", &t[i].d[], &t[i].d[]);
rt = build(, n, , );
while(m--){
int op;
scanf("%d%d%d", &op, &x, &y);
if(op == ){
n++;
t[n].l = t[n].r = ;
t[n].Max[] = t[n].Min[] = t[n].d[] = x;
t[n].Max[] = t[n].Min[] = t[n].d[] = y;
ins(rt, , n);
}
else{
ans = inf;
query(rt);
printf("%lld\n", ans);
}
}
return ;
}
BZOJ3053
题意:k维坐标系下的最近的m个点。直接对于每一个询问都在kdtree中询问m次最近点,每次找到一个最近点对需要把它记录下来,用堆维护即可。
#include <bits/stdc++.h>
#define ll long long
#define mp make_pair using namespace std;
#define N 50010
const ll inf = 1e18;
int n,m,k,i,id[N],root,cmp_d,rt;
int x, y, num;
struct node{int d[],l,r,Max[],Min[],val,sum,f;}t[N];
bool cmp(const node&a,const node&b){return a.d[cmp_d]<b.d[cmp_d];}
void umax(int&a,int b){if(a<b)a=b;}
void umin(int&a,int b){if(a>b)a=b;}
void up(int x){
for(int i = ; i < k; i++){
if(t[x].l){
umax(t[x].Max[i],t[t[x].l].Max[i]);
umin(t[x].Min[i],t[t[x].l].Min[i]);
}
if(t[x].r){
umax(t[x].Max[i],t[t[x].r].Max[i]);
umin(t[x].Min[i],t[t[x].r].Min[i]);
}
}
}
int build(int l,int r,int D,int f){
int mid=(l+r)>>;
cmp_d=D,std::nth_element(t+l,t+mid,t+r+,cmp);
id[t[mid].f]=mid;
t[mid].f=f;
for(int i = ; i < k; i++)
t[mid].Max[i]=t[mid].Min[i]=t[mid].d[i];
//t[mid].Max[1]=t[mid].Min[1]=t[mid].d[1];
//t[mid].val=t[mid].sum=0;
if(l!=mid)t[mid].l=build(l,mid-,(D+)%k,mid);else t[mid].l=;
if(r!=mid)t[mid].r=build(mid+,r,(D+)%k,mid);else t[mid].r=;
return up(mid),mid;
}
int qx[];
ll dis(int p){//估价函数, 以p为子树的最小距离
ll ret = , ans = ;
for(int i = ; i < k; i++) {
ret = ;
if(t[p].Max[i] < qx[i]) ret = qx[i]-t[p].Max[i];
if(t[p].Min[i] > qx[i]) ret = t[p].Min[i]-qx[i];
ans += ret*ret;
}
return ans;
}
ll getdis(int p){
ll ans = ;
for(int i = ; i < k; i++)
ans += (qx[i]-t[p].d[i])*(qx[i]-t[p].d[i]);
return ans;
}
void ins(int now, int k, int x){
if(t[x].d[k] >= t[now].d[k]){
if(t[now].r) ins(t[now].r, !k, x);
else
t[now].r = x, t[x].f = now;
}
else {
if(t[now].l) ins(t[now].l, !k, x);
else t[now].l = x, t[x].f = now;
}
up(now);
}
ll ret;
multiset< pair<int, int> > ans;
void query(int p){
ll dl = inf, dr = inf, d = getdis(p);
ans.insert( mp((int)d, p) );
if(ans.size() > num){
multiset< pair<int, int> >::iterator it = ans.end();
it--;
ans.erase(it);
}
ret = (*ans.rbegin()).first;
if(t[p].l) dl = dis(t[p].l);
if(t[p].r) dr = dis(t[p].r);
if(dl < dr){
if(dl < ret||ans.size() < num) query(t[p].l);
if(dr < ret||ans.size() < num) query(t[p].r);
}
else {
if(dr < ret||ans.size() < num) query(t[p].r);
if(dl < ret||ans.size() < num) query(t[p].l);
}
} int main(){
while(~scanf("%d%d", &n, &k)){
for(int i = ; i <= n; i++){
for(int j = ; j < k; j++)
scanf("%d", &t[i].d[j]);
}
rt = build(, n, , );
scanf("%d", &m);
while(m--){
for(int i = ; i < k; i++)
scanf("%d", qx+i);
scanf("%d", &num);
ans.clear();
query(rt);
printf ("the closest %d points are:\n", num);
for(multiset< pair<int, int> >::iterator it = ans.begin(); it != ans.end(); it++){
int pos = (*it).second;
for(int i = ; i < k; i++)
printf("%d%c", t[pos].d[i], " \n"[i == k-]);
}
}
}
return ;
}
kd tree学习笔记 (最近邻域查询)的更多相关文章
- k-d tree 学习笔记
以下是一些奇怪的链接有兴趣的可以看看: https://blog.sengxian.com/algorithms/k-dimensional-tree http://zgjkt.blog.uoj.ac ...
- K-D Tree学习笔记
用途 做各种二维三维四维偏序等等. 代替空间巨大的树套树. 数据较弱的时候水分. 思想 我们发现平衡树这种东西功能强大,然而只能做一维上的询问修改,显得美中不足. 于是我们尝试用平衡树的这种二叉树结构 ...
- MyBatis:学习笔记(3)——关联查询
MyBatis:学习笔记(3)--关联查询 关联查询 理解联结 SQL最强大的功能之一在于我们可以在数据查询的执行中可以使用联结,来将多个表中的数据作为整体进行筛选. 模拟一个简单的在线商品购物系统, ...
- mybatis学习笔记(10)-一对一查询
mybatis学习笔记(10)-一对一查询 标签: mybatis mybatis学习笔记10-一对一查询 resultType实现 resultMap实现 resultType和resultMap实 ...
- 珂朵莉树(Chtholly Tree)学习笔记
珂朵莉树(Chtholly Tree)学习笔记 珂朵莉树原理 其原理在于运用一颗树(set,treap,splay......)其中要求所有元素有序,并且支持基本的操作(删除,添加,查找......) ...
- dsu on tree学习笔记
前言 一次模拟赛的\(T3\):传送门 只会\(O(n^2)\)的我就\(gg\)了,并且对于题解提供的\(\text{dsu on tree}\)的做法一脸懵逼. 看网上的其他大佬写的笔记,我自己画 ...
- SQLServer学习笔记<>相关子查询及复杂查询
二.查询缺少值的查询 在这里我们加入要查询2008年每一天的订单有多少?首先我们可以查询下订单表的订单日期在2008年的所有订单信息. 1 select distinct orderdate,coun ...
- Hibernate学习笔记-Hibernate HQL查询
Session是持久层操作的基础,相当于JDBC中的Connection,通过Session会话来保存.更新.查找数据.session是Hibernate运作的中心,对象的生命周期.事务的管理.数据库 ...
- Link Cut Tree学习笔记
从这里开始 动态树问题和Link Cut Tree 一些定义 access操作 换根操作 link和cut操作 时间复杂度证明 Link Cut Tree维护链上信息 Link Cut Tree维护子 ...
随机推荐
- C#对泛型List<T>系列化与反系列化
练习一个小例子,在C#中,怎样对泛型List<T>数据集进行系列化与反系列化.我们先了解msdn提供的JavaScriptSerializer类: JavaScriptSerializer ...
- apk反编译、smali修改、回编译笔记
最近下了一个apk程序,但是一启动会弹出一个流氓广告.这个广告不是原厂商加的,而是有人在原有apk程序的基础上,加了一个壳,让apk先启动他加的广告,再启动原来的程序,很恶心.于是想去掉它. 试了几个 ...
- Spark on YARN两种运行模式介绍
本文出自:Spark on YARN两种运行模式介绍http://www.aboutyun.com/thread-12294-1-1.html(出处: about云开发) 问题导读 1.Spark ...
- momentjs 求小时差异
momentjs 使用 var now1 = moment( moment().unix()*1000); //获取unix时间戳 需要*1000 var befor_time = moment(1 ...
- C语言 ---- 函数 结构体 iOS学习-----细碎知识点总结
函数的定义 返回值类型 函数名(形式参数列表) { 函数的实现 } 函数不允许嵌套定义 如果函数的定义在主调函数之后,那么要进行提前声明才能使用. // 匿名结构体,结构 ...
- ActiveMq池
有两种连接方式 1.Spring 引用 <!-- 配置JMS连接工厂 --> <bean id="connectionFactory" class=" ...
- SQL Server 2008 修改安装路径后安装出错的解决方法
1.安装时如果修改安装路径后报错 例如想把“C:\Program Files\Microsoft SQL Server” 修改为“D:\Program Files\Microsoft SQL Serv ...
- Struts2(二):工作原理
struts可查看源码:https://github.com/apache/struts 在学习struts2之前,我先看了一些比较早版本对struts2的工作原理相关的介绍,顺便抄写过来,用来帮助自 ...
- Velocity(6)——#if指令
下面是#If指令的一个简单而完整的示例: #if ($foo < 10) Go North #elseif ($foo == 10) Go East #else Go West#end 不能漏掉 ...
- 字符编码 and cpp
预备知识 字符:抽象的最小文本单位.仅代表符合没有实际意义(如:¥, a, 国) 字符集:字符的集合(如gb2312, ASCII, UNICODE) 编码:是对字符集的描述,计算机要准确的处理各种字 ...