2
6
2013
0

Kd-Tree简单实现

这坑爹玩意……这样能删能插了。。

ps:我居然把分裂点看反了!!

pps:删除节点之后我居然忘记上传了!!(虽然按照这颗树的尿性很快就会上传上去。。)

代码有错……nth_element是左闭右开的

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
const double eps=1e-12;
const int poolsize = 10000;
const double alpha = 0.7;
class kdtree
{
public:
	struct pointF
	{
		double d[2];
		inline bool operator == (const pointF &a){
			return (abs(a.d[0]-d[0])<=eps && abs(a.d[1]-d[1])<=eps);
		}
	};
private:
	struct node
	{
		kdtree *t;
		pointF split_node;
		int split_method;
		node*ch[2];
		node*left(){return ch[0];}; // < split_pos
		node*right(){return ch[1];};// > split_pos
		int size;
		bool exist;
		void rebuild();
		void rebuild_dfs(node *n,pointF* ps,int &size);
		bool judge(){
			if (!ch[0]||!ch[1]) return false;
			return !size || ch[0]->size > alpha * size || ch[1]->size > alpha * size;
		};
		node(){
			size=1;
			exist=true;
			//if (judge()) rebuild(); 
		}

	}*top,node_pool[poolsize],*alloca_reuse[poolsize];int pool;int stack_size;

	node * alloca_node(){
		if (stack_size){
			// 优先从回收池中取出
			node_pool[stack_size].t = this;node_pool[stack_size].size=1;node_pool[stack_size].exist=true;
			return alloca_reuse[stack_size--];
		}
		else
		{
			node_pool[pool].t = this;node_pool[pool].size=1;node_pool[pool].exist=true;
			return &node_pool[pool++];
		}
	};
	void release_node(node *n){
		memset(n,0,sizeof(node));
		alloca_reuse[stack_size++]=n;
	};
public:
	void build(pointF *_ps,int _size); // ps => [1,n]
	pointF search(pointF x);
	kdtree(){
		top=0;pool=0;ps=0;builded=false;sort_by=0;stack_size=0;
		memset(node_pool,0,sizeof(node_pool));
		memset(alloca_reuse,0,sizeof(alloca_reuse));
	};
	void clean(){
		top=0;pool=0;ps=0;builded=false;sort_by=0;stack_size=0;
		memset(node_pool,0,sizeof(node_pool));
		memset(alloca_reuse,0,sizeof(alloca_reuse));
		
	};
	void clean_fast(){
		top=0;pool=0;ps=0;n=0;builded=false;sort_by=0;stack_size=0;
		memset(node_pool,0,sizeof(node_pool));
		memset(alloca_reuse,0,sizeof(alloca_reuse));
	};
	bool remove(pointF p);
	void insert(pointF p);
	void select(pointF lt,pointF rb,vector<pointF> &target);
private:
	pointF *ps;
	pointF filter;
	pointF result;
	bool builded;
	int n;
	node* build_dfs(int l,int r);
	void search_dfs(node* n,bool force,bool lazy);
	void insert_dfs(node* n,bool mt,bool re_lazy);
	void select_dfs(node* n,pointF <,pointF& rb,vector<pointF> &target);
	node *ansp;
	double ansd;
	inline double Variance(int l,int r,int d){
		double variance = 0;
		double Average = 0;
		for (int i=l;i<=r;i++) Average+=ps[i].d[d];
		Average/=(r-l+1.0);
		for (int i=l;i<=r;i++) variance+=(ps[i].d[d]-Average)*(ps[i].d[d]-Average);
		variance/=(r-l+1.0);
		return variance;
	};
	int sort_by;
	class sort_compare{
	private:
		int sort_by;
	public:
		bool operator()(const pointF & x, const pointF & y){
			return x.d[sort_by]<y.d[sort_by];
		};
		sort_compare(int _sort_by){
			sort_by=_sort_by;
		}
	};	
	inline double distance(const pointF & x,const pointF & y){
		return (x.d[0]-y.d[0])*(x.d[0]-y.d[0]) +  (x.d[1]-y.d[1])*(x.d[1]-y.d[1]);
	}
}T;
void kdtree::build(pointF *_ps,int _size){
	if (builded) throw "Cannot build a kd-tree again before you clean it!!";
	builded=true;
	ps=_ps;n=_size;
	top=build_dfs(1,n);
};
kdtree::node* kdtree::build_dfs(int l,int r){ // [l,r]
	if (r-l<0) return NULL;
	node * range = alloca_node();range->exist=true;
	int mid = (l+r)>>1;
	double VarianceX = Variance(l,r,0);
	double VarianceY = Variance(l,r,1);
	int split_method_i = VarianceY>VarianceX;
	range->split_method = split_method_i;
	sort_by = split_method_i;
	nth_element(ps+l,ps+mid,ps+r+1,sort_compare(sort_by));
	//sort(ps+l,ps+r,sort_compare(sort_by));
	range->split_node = *(ps+mid);
	range->ch[0]= build_dfs(l,mid-1);
	range->ch[1]= build_dfs(mid+1,r);
	range->size = ((range->ch[0])?(range->ch[0]->size):(0))+((range->ch[1])?(range->ch[1]->size):(0)) + (range->exist);
	return range;
};
kdtree::pointF kdtree::search(pointF x){
	if (!builded) throw "Cannot work on empty tree!!";
	filter=x;
	ansd = 1e307;
	search_dfs(top,false,false);
	return ansp->split_node;
};
bool kdtree::remove(pointF x){
	/* 刚调整过,本来删完忘记上传size修改了,这样应该可以……
	 *	但是一次修改造成的不平衡只有下次访问到才会修复……
	 */
	if (!builded) throw "Cannot work on empty tree!!";
	filter=x;
	ansd = 1e307;
	search_dfs(top,true,false);
	return ansd==0;
}
void kdtree::search_dfs(node* n,bool force,bool lazy){
	if (!n) return ;
	double dist=0;
	dist =distance(n->split_node,filter);
	if ((dist<ansd && n->exist && !force) || (force && n->split_node==filter)){
		ansd=dist;
		ansp=n;
		if (force) {
			ansp->exist=false;
			ansp->size--;
			return;
		}
	}
	int sm=n->split_method;
	bool rebuild=n->judge();
	double radius = (filter.d[sm]-n->split_node.d[sm])*(filter.d[sm]-n->split_node.d[sm]);
	if (filter.d[sm]-n->split_node.d[sm]<=eps){
		// left
		search_dfs(n->ch[0],force,lazy|rebuild);
		if (ansd<=radius) search_dfs(n->ch[1],force,lazy|rebuild);
	}else{
		// right
		search_dfs(n->ch[1],force,lazy|rebuild);
		if (ansd<=radius) search_dfs(n->ch[0],force,lazy|rebuild);
	}
	n->size = ((n->ch[0])?(n->ch[0]->size):(0))+((n->ch[1])?(n->ch[1]->size):(0)) + (n->exist);
	if (rebuild &!lazy) n->rebuild(); // 重构这棵树
}

void kdtree::insert(pointF p){
	filter = p;
	insert_dfs(top,0,0);
}

void kdtree::insert_dfs(node* n,bool mt,bool re_lazy){
	/*
	 *	同样……一次修改之后不会马上重构。。
	 */
	int sm=n->split_method;
	double radius = (filter.d[sm]-n->split_node.d[sm])*(filter.d[sm]-n->split_node.d[sm]);
	bool re=n->judge();
	int belong = (filter.d[sm]-n->split_node.d[sm]>=eps);  //target.pos>split => 1
	if (n->ch[belong]!=NULL){
		insert_dfs(n->ch[belong],!mt,re|re_lazy);
	}else{
		n->ch[belong]=alloca_node();
		n->ch[belong]->split_method = mt;
		n->ch[belong]->split_node = filter;
	}
	n->size = ((n->ch[0])?(n->ch[0]->size):(0))+((n->ch[1])?(n->ch[1]->size):(0)) + (n->exist);
	if (!re_lazy && re) n->rebuild();
};
void kdtree::node::rebuild(){
	int size=1;
	rebuild_dfs(this,t->ps,size);size--;
	node *va = t->build_dfs(1,size);
	memcpy(this,va,sizeof(node));
	t->release_node(va);
};
void kdtree::node::rebuild_dfs(node *n,pointF* ps,int &size){
	if (!n) return;
	if (n->exist){
		ps[size++]=n->split_node;
	}
	rebuild_dfs(n->ch[0],ps,size);
	rebuild_dfs(n->ch[1],ps,size);
	if (n!=this) t->release_node(n);
};
void kdtree::select(pointF lt,pointF rt,vector<pointF> &target){
	select_dfs(top,lt,rt,target);
};
void kdtree::select_dfs(node* n,pointF <,pointF &rb,vector<pointF> &target){
	/*
	 *	关于这里的lt、rb或许重构为degree的范围更好?
	 */
	if (!n) return;
	if (n->size==0) return ;
	pointF p = n->split_node;
	if (n->exist && p.d[0]>=lt.d[0] && // 大于左上的x
					p.d[0]<=rb.d[0] && // 小于右下的x
					p.d[1]<=lt.d[1] && // 小于左上的y
					p.d[1]>=rb.d[1])   // 大于右下的y
					target.push_back(p);
	if (n->split_method==0){
		if (lt.d[0] <= p.d[0]) select_dfs(n->ch[0],lt,rb,target); // left
		if (rb.d[0] >= p.d[0]) select_dfs(n->ch[1],lt,rb,target); // right
	} else {
		if (rb.d[1] <= p.d[1]) select_dfs(n->ch[0],lt,rb,target); // bottom
		if (lt.d[1] >= p.d[1]) select_dfs(n->ch[1],lt,rb,target); // top
	}
};
kdtree::pointF points[500]; // buffers
int main(){
	int n,q;
	scanf("%d%d",&n,&q);
	for (int i=1;i<=n;i++) scanf("%lf%lf",&points[i].d[0],&points[i].d[1]);
	T.build(points,n);
	while (q--){
		kdtree::pointF lt;
		kdtree::pointF rt;
		int x;scanf("%d",&x);
		if (x==1){
			scanf("%lf%lf",<.d[0],<.d[1]);
			scanf("%lf%lf",&rt.d[0],&rt.d[1]);
			vector<kdtree::pointF> v;
			T.select(lt,rt,v);
			for (vector<kdtree::pointF>::iterator it=v.begin();it!=v.end();it++)
				printf("(%.2lf %.2lf)\n",it->d[0],it->d[1]);
		}else{
			scanf("%lf%lf",<.d[0],<.d[1]);
			if (x==2)
				T.remove(lt);
			else
				T.insert(lt);
		}
	}
	system("pause");
};
Category: OI | Tags: 计算几何

Host by is-Programmer.com | Power by Chito 1.3.3 beta | Theme: Aeros 2.0 by TheBuckmaker.com