四叉树空间索引原理及其实现

2023-05-16

今天依然在放假中,在此将以前在学校写的四叉树的东西拿出来和大家分享。

四叉树索引的基本思想是将地理空间递归划分为不同层次的树结构。它将已知范围的空间等分成四个相等的子空间,如此递归下去,直至树的层次达到一定深度或者满足某种要求后停止分割。四叉树的结构比较简单,并且当空间数据对象分布比较均匀时,具有比较高的空间数据插入和查询效率,因此四叉树是GIS中常用的空间索引之一。常规四叉树的结构如所示,地理空间对象都存储在叶子节点上,中间节点以及根节点不存储地理空间对象。

 

四叉树示意图

 

四叉树对于区域查询,效率比较高。但如果空间对象分布不均匀,随着地理空间对象的不断插入,四叉树的层次会不断地加深,将形成一棵严重不平衡的四叉树,那么每次查询的深度将大大的增多,从而导致查询效率的急剧下降。

 

本节将介绍一种改进的四叉树索引结构。四叉树结构是自顶向下逐步划分的一种树状的层次结构。传统的四叉树索引存在着以下几个缺点:

(1)空间实体只能存储在叶子节点中,中间节点以及根节点不能存储空间实体信息,随着空间对象的不断插入,最终会导致四叉树树的层次比较深,在进行空间数据窗口查询的时候效率会比较低下。

(2)同一个地理实体在四叉树的分裂过程中极有可能存储在多个节点中,这样就导致了索引存储空间的浪费。

(3)由于地理空间对象可能分布不均衡,这样会导致常规四叉树生成一棵极为不平衡的树,这样也会造成树结构的不平衡以及存储空间的浪费。

相应的改进方法,将地理实体信息存储在完全包含它的最小矩形节点中,不存储在它的父节点中,每个地理实体只在树中存储一次,避免存储空间的浪费。首先生成满四叉树,避免在地理实体插入时需要重新分配内存,加快插入的速度,最后将空的节点所占内存空间释放掉。改进后的四叉树结构如下图所示。四叉树的深度一般取经验值4-7之间为最佳。

 

改进的四叉树结构

 

为了维护空间索引与对存储在文件或数据库中的空间数据的一致性,作者设计了如下的数据结构支持四叉树的操作。

(1)四分区域标识

分别定义了一个平面区域的四个子区域索引号,右上为第一象限0,左上为第二象限1,左下为第三象限2,右下为第四象限3

typedef enum

{

      UR = 0,// UR第一象限

      UL = 1, // UL为第二象限

      LL = 2, // LL为第三象限

      LR = 3  // LR为第四象限

}QuadrantEnum;

(2)空间对象数据结构

空间对象数据结构是对地理空间对象的近似,在空间索引中,相当一部分都是采用MBR作为近似。

/*空间对象MBR信息*/

typedef struct SHPMBRInfo

{

      int nID;       //空间对象ID

      MapRect Box;    //空间对象MBR范围坐标

}SHPMBRInfo;

nID是空间对象的标识号,Box是空间对象的最小外包矩形(MBR)。

(3)四叉树节点数据结构

四叉树节点是四叉树结构的主要组成部分,主要用于存储空间对象的标识号和MBR,也是四叉树算法操作的主要部分。

/*四叉树节点类型结构*/

typedef struct QuadNode

{

      MapRect            Box;                   //节点所代表的矩形区域

      int                nShpCount;        //节点所包含的所有空间对象个数

      SHPMBRInfo* pShapeObj;          //空间对象指针数组

      int         nChildCount;            //子节点个数

      QuadNode *children[4];             //指向节点的四个孩子

}QuadNode;

Box是代表四叉树对应区域的最小外包矩形,上一层的节点的最小外包矩形包含下一层最小外包矩形区域;nShpCount代表本节点包含的空间对象的个数;pShapeObj代表指向空间对象存储地址的首地址,同一个节点的空间对象在内存中连续存储;nChildCount代表节点拥有的子节点的数目;children是指向孩子节点指针的数组。

上述理论部分都都讲的差不多了,下面就贴上我的C语言实现版本代码。

头文件如下:

#ifndef __QUADTREE_H_59CAE94A_E937_42AD_AA27_794E467715BB__
#define __QUADTREE_H_59CAE94A_E937_42AD_AA27_794E467715BB__




/* 一个矩形区域的象限划分::

UL(1)   |    UR(0)
----------|-----------
LL(2)   |    LR(3)
以下对该象限类型的枚举
*/
typedef enum
{
	UR = 0,
	UL = 1,
	LL = 2,
	LR = 3
}QuadrantEnum;

/*空间对象MBR信息*/
typedef struct SHPMBRInfo
{
	int nID;		//空间对象ID号
	MapRect Box;	//空间对象MBR范围坐标
}SHPMBRInfo;

/* 四叉树节点类型结构 */
typedef struct QuadNode
{
	MapRect		Box;			//节点所代表的矩形区域
	int			nShpCount;		//节点所包含的所有空间对象个数
	SHPMBRInfo* pShapeObj;		//空间对象指针数组
	int		nChildCount;		//子节点个数
	QuadNode  *children[4];		//指向节点的四个孩子 
}QuadNode;

/* 四叉树类型结构 */
typedef struct quadtree_t
{
	QuadNode  *root;
	int         depth;           // 四叉树的深度                    
}QuadTree;


	//初始化四叉树节点
	QuadNode *InitQuadNode();

	//层次创建四叉树方法(满四叉树)
	void CreateQuadTree(int depth,GeoLayer *poLayer,QuadTree* pQuadTree);

	//创建各个分支
	void CreateQuadBranch(int depth,MapRect &rect,QuadNode** node);

	//构建四叉树空间索引
	void BuildQuadTree(GeoLayer*poLayer,QuadTree* pQuadTree);

	//四叉树索引查询(矩形查询)
	void SearchQuadTree(QuadNode* node,MapRect &queryRect,vector<int>& ItemSearched);

	//四叉树索引查询(矩形查询)并行查询
	void SearchQuadTreePara(vector<QuadNode*> resNodes,MapRect &queryRect,vector<int>& ItemSearched);

	//四叉树的查询(点查询)
	void PtSearchQTree(QuadNode* node,double cx,double cy,vector<int>& ItemSearched);

	//将指定的空间对象插入到四叉树中
	void Insert(long key,MapRect &itemRect,QuadNode* pNode);

	//将指定的空间对象插入到四叉树中
	void InsertQuad(long key,MapRect &itemRect,QuadNode* pNode);

	//将指定的空间对象插入到四叉树中
	void InsertQuad2(long key,MapRect &itemRect,QuadNode* pNode);

	//判断一个节点是否是叶子节点
	bool IsQuadLeaf(QuadNode* node);

	//删除多余的节点
	bool DelFalseNode(QuadNode* node);

	//四叉树遍历(所有要素)
	void TraversalQuadTree(QuadNode* quadTree,vector<int>& resVec);

	//四叉树遍历(所有节点)
	void TraversalQuadTree(QuadNode* quadTree,vector<QuadNode*>& arrNode);

	//释放树的内存空间
	void ReleaseQuadTree(QuadNode** quadTree);

	//计算四叉树所占的字节的大小
	long CalByteQuadTree(QuadNode* quadTree,long& nSize);


#endif


源文件如下:

#include "QuadTree.h"


QuadNode *InitQuadNode()
{
	QuadNode *node = new QuadNode;
	node->Box.maxX = 0;
	node->Box.maxY = 0;
	node->Box.minX = 0;
	node->Box.minY = 0;

	for (int i = 0; i < 4; i ++)
	{
		node->children[i] = NULL;
	}
	node->nChildCount = 0;
	node->nShpCount = 0;
	node->pShapeObj = NULL;

	return node;
}

void CreateQuadTree(int depth,GeoLayer *poLayer,QuadTree* pQuadTree)
{
	pQuadTree->depth = depth;

	GeoEnvelope env;	//整个图层的MBR
	poLayer->GetExtent(&env);
	
	MapRect rect;
	rect.minX = env.MinX;
	rect.minY = env.MinY;
	rect.maxX = env.MaxX;
	rect.maxY = env.MaxY;
	
	//创建各个分支
	CreateQuadBranch(depth,rect,&(pQuadTree->root));

	int nCount = poLayer->GetFeatureCount();
	GeoFeature **pFeatureClass = new GeoFeature*[nCount];
	for (int i = 0; i < poLayer->GetFeatureCount(); i ++)
	{
		pFeatureClass[i] = poLayer->GetFeature(i); 
	}

	//插入各个要素
	GeoEnvelope envObj;	//空间对象的MBR
	//#pragma omp parallel for
	for (int i = 0; i < nCount; i ++)
	{
		pFeatureClass[i]->GetGeometry()->getEnvelope(&envObj);
		rect.minX = envObj.MinX;
		rect.minY = envObj.MinY;
		rect.maxX = envObj.MaxX;
		rect.maxY = envObj.MaxY;
		InsertQuad(i,rect,pQuadTree->root);
	}

	//DelFalseNode(pQuadTree->root);
}

void CreateQuadBranch(int depth,MapRect &rect,QuadNode** node)
{
	if (depth != 0)
	{
		*node = InitQuadNode();	//创建树根
		QuadNode *pNode = *node;
		pNode->Box = rect;
		pNode->nChildCount = 4;

		MapRect boxs[4];
		pNode->Box.Split(boxs,boxs+1,boxs+2,boxs+3);
		for (int i = 0; i < 4; i ++)
		{
			//创建四个节点并插入相应的MBR
			pNode->children[i] = InitQuadNode();
			pNode->children[i]->Box = boxs[i];

			CreateQuadBranch(depth-1,boxs[i],&(pNode->children[i]));
		}
	}
}

void BuildQuadTree(GeoLayer *poLayer,QuadTree* pQuadTree)
{
	assert(poLayer);
	GeoEnvelope env;	//整个图层的MBR
	poLayer->GetExtent(&env);
	pQuadTree->root = InitQuadNode();

	QuadNode* rootNode = pQuadTree->root;

	rootNode->Box.minX = env.MinX;
	rootNode->Box.minY = env.MinY;
	rootNode->Box.maxX = env.MaxX;
	rootNode->Box.maxY = env.MaxY;

	//设置树的深度(	根据等比数列的求和公式)
	//pQuadTree->depth = log(poLayer->GetFeatureCount()*3/8.0+1)/log(4.0);
	int nCount = poLayer->GetFeatureCount();

	MapRect rect;
	GeoEnvelope envObj;	//空间对象的MBR
	for (int i = 0; i < nCount; i ++)
	{
		poLayer->GetFeature(i)->GetGeometry()->getEnvelope(&envObj);
		rect.minX = envObj.MinX;
		rect.minY = envObj.MinY;
		rect.maxX = envObj.MaxX;
		rect.maxY = envObj.MaxY;
		InsertQuad2(i,rect,rootNode);
	}

	DelFalseNode(pQuadTree->root);
}

void SearchQuadTree(QuadNode* node,MapRect &queryRect,vector<int>& ItemSearched)
{
	assert(node);

	//int coreNum = omp_get_num_procs();
	//vector<int> * pResArr = new vector<int>[coreNum];

	if (NULL != node)
	{
		for (int i = 0; i < node->nShpCount; i ++)
		{
			if (queryRect.Contains(node->pShapeObj[i].Box)
				|| queryRect.Intersects(node->pShapeObj[i].Box))
			{
				ItemSearched.push_back(node->pShapeObj[i].nID);
			}
		}

		//并行搜索四个孩子节点
		/*#pragma omp parallel sections
		{
			#pragma omp section
			if ((node->children[0] != NULL) && 
				(node->children[0]->Box.Contains(queryRect)
				|| node->children[0]->Box.Intersects(queryRect)))
			{
				int tid = omp_get_thread_num();
				SearchQuadTree(node->children[0],queryRect,pResArr[tid]);
			}

			#pragma omp section
			if ((node->children[1] != NULL) && 
				(node->children[1]->Box.Contains(queryRect)
				|| node->children[1]->Box.Intersects(queryRect)))
			{
				int tid = omp_get_thread_num();
				SearchQuadTree(node->children[1],queryRect,pResArr[tid]);
			}

			#pragma omp section
			if ((node->children[2] != NULL) && 
				(node->children[2]->Box.Contains(queryRect)
				|| node->children[2]->Box.Intersects(queryRect)))
			{
				int tid = omp_get_thread_num();
				SearchQuadTree(node->children[2],queryRect,pResArr[tid]);
			}

			#pragma omp section
			if ((node->children[3] != NULL) && 
				(node->children[3]->Box.Contains(queryRect)
				|| node->children[3]->Box.Intersects(queryRect)))
			{
				int tid = omp_get_thread_num();
				SearchQuadTree(node->children[3],queryRect,pResArr[tid]);
			}
		}*/
		for (int i = 0; i < 4; i ++)
		{
			if ((node->children[i] != NULL) && 
				(node->children[i]->Box.Contains(queryRect)
				|| node->children[i]->Box.Intersects(queryRect)))
			{
				SearchQuadTree(node->children[i],queryRect,ItemSearched);
				//node = node->children[i];	//非递归
			}
		}
	}

	/*for (int i = 0 ; i < coreNum; i ++)
	{
		ItemSearched.insert(ItemSearched.end(),pResArr[i].begin(),pResArr[i].end());
	}*/

}

void SearchQuadTreePara(vector<QuadNode*> resNodes,MapRect &queryRect,vector<int>& ItemSearched)
{
	int coreNum = omp_get_num_procs();
	omp_set_num_threads(coreNum);
	vector<int>* searchArrs = new vector<int>[coreNum];
	for (int i = 0; i < coreNum; i ++)
	{
		searchArrs[i].clear();
	}

	#pragma omp parallel for
	for (int i = 0; i < resNodes.size(); i ++)
	{
		int tid = omp_get_thread_num();
		for (int j = 0; j < resNodes[i]->nShpCount; j ++)
		{
			if (queryRect.Contains(resNodes[i]->pShapeObj[j].Box)
				|| queryRect.Intersects(resNodes[i]->pShapeObj[j].Box))
			{
				searchArrs[tid].push_back(resNodes[i]->pShapeObj[j].nID);
			}
		}
	}

	for (int i = 0; i < coreNum; i ++)
	{
		ItemSearched.insert(ItemSearched.end(),
			searchArrs[i].begin(),searchArrs[i].end());
	}

	delete [] searchArrs;
	searchArrs = NULL;
}

void PtSearchQTree(QuadNode* node,double cx,double cy,vector<int>& ItemSearched)
{
	assert(node);
	if (node->nShpCount >0)		//节点		  
	{
		for (int i = 0; i < node->nShpCount; i ++)
		{
			if (node->pShapeObj[i].Box.IsPointInRect(cx,cy))
			{
				ItemSearched.push_back(node->pShapeObj[i].nID);
			}
		}
	}

	else if (node->nChildCount >0)				//节点
	{
		for (int i = 0; i < 4; i ++)
		{
			if (node->children[i]->Box.IsPointInRect(cx,cy))
			{
				PtSearchQTree(node->children[i],cx,cy,ItemSearched);
			}
		}
	}

	//找出重复元素的位置
	sort(ItemSearched.begin(),ItemSearched.end());	//先排序,默认升序
	vector<int>::iterator unique_iter = 
		unique(ItemSearched.begin(),ItemSearched.end());
	ItemSearched.erase(unique_iter,ItemSearched.end());
}

void Insert(long key, MapRect &itemRect,QuadNode* pNode)
{
	QuadNode *node = pNode;		//保留根节点副本
	SHPMBRInfo pShpInfo;
	
	//节点有孩子
	if (0 < node->nChildCount)
	{
		for (int i = 0; i < 4; i ++)
		{  
			//如果包含或相交,则将节点插入到此节点
			if (node->children[i]->Box.Contains(itemRect)
				|| node->children[i]->Box.Intersects(itemRect))
			{
				//node = node->children[i];
				Insert(key,itemRect,node->children[i]);
			}
		}
	}

	//如果当前节点存在一个子节点时
	else if (1 == node->nShpCount)
	{
		MapRect boxs[4];
		node->Box.Split(boxs,boxs+1,boxs+2,boxs+3);

		//创建四个节点并插入相应的MBR
		node->children[UR] = InitQuadNode();
		node->children[UL] = InitQuadNode();
		node->children[LL] = InitQuadNode();
		node->children[LR] = InitQuadNode();

		node->children[UR]->Box = boxs[0];
		node->children[UL]->Box = boxs[1];
		node->children[LL]->Box = boxs[2];
		node->children[LR]->Box = boxs[3];
		node->nChildCount = 4;

		for (int i = 0; i < 4; i ++)
		{  
			//将当前节点中的要素移动到相应的子节点中
			for (int j = 0; j < node->nShpCount; j ++)
			{
				if (node->children[i]->Box.Contains(node->pShapeObj[j].Box)
					|| node->children[i]->Box.Intersects(node->pShapeObj[j].Box))
				{
					node->children[i]->nShpCount += 1;
					node->children[i]->pShapeObj = 
						(SHPMBRInfo*)malloc(node->children[i]->nShpCount*sizeof(SHPMBRInfo));
					
					memcpy(node->children[i]->pShapeObj,&(node->pShapeObj[j]),sizeof(SHPMBRInfo));

					free(node->pShapeObj);
					node->pShapeObj = NULL;
					node->nShpCount = 0;
				}
			}
		}

		for (int i = 0; i < 4; i ++)
		{  
			//如果包含或相交,则将节点插入到此节点
			if (node->children[i]->Box.Contains(itemRect)
				|| node->children[i]->Box.Intersects(itemRect))
			{
				if (node->children[i]->nShpCount == 0)	 //如果之前没有节点
				{
					node->children[i]->nShpCount += 1;
					node->pShapeObj = 
						(SHPMBRInfo*)malloc(sizeof(SHPMBRInfo)*node->children[i]->nShpCount);
				}
				else if	(node->children[i]->nShpCount > 0)
				{
					node->children[i]->nShpCount += 1;
					node->children[i]->pShapeObj = 
						(SHPMBRInfo *)realloc(node->children[i]->pShapeObj,
						sizeof(SHPMBRInfo)*node->children[i]->nShpCount);
				}

				pShpInfo.Box = itemRect;
				pShpInfo.nID = key;
				memcpy(node->children[i]->pShapeObj,
					&pShpInfo,sizeof(SHPMBRInfo));
			}
		}
	}

	//当前节点没有空间对象
	else if (0 == node->nShpCount)
	{
		node->nShpCount += 1;
		node->pShapeObj = 
			(SHPMBRInfo*)malloc(sizeof(SHPMBRInfo)*node->nShpCount);

		pShpInfo.Box = itemRect;
		pShpInfo.nID = key;
		memcpy(node->pShapeObj,&pShpInfo,sizeof(SHPMBRInfo));
	}
}

void InsertQuad(long key,MapRect &itemRect,QuadNode* pNode)
{
	assert(pNode != NULL);

	if (!IsQuadLeaf(pNode))	   //非叶子节点
	{
		int nCorver = 0;		//跨越的子节点个数
		int iIndex = -1;		//被哪个子节点完全包含的索引号
		for (int i = 0; i < 4; i ++)
		{
			if (pNode->children[i]->Box.Contains(itemRect)
				&& pNode->Box.Contains(itemRect))
			{
				nCorver += 1;
				iIndex = i;
			}
		}

		//如果被某一个子节点包含,则进入该子节点
		if (/*pNode->Box.Contains(itemRect) || 
			pNode->Box.Intersects(itemRect)*/1 <= nCorver)
		{ 
			InsertQuad(key,itemRect,pNode->children[iIndex]);
		}

		//如果跨越了多个子节点,直接放在这个节点中
		else if (nCorver == 0)
		{
			if (pNode->nShpCount == 0)	 //如果之前没有节点
			{
				pNode->nShpCount += 1;
				pNode->pShapeObj = 
					(SHPMBRInfo*)malloc(sizeof(SHPMBRInfo)*pNode->nShpCount);
			}
			else
			{
				pNode->nShpCount += 1;
				pNode->pShapeObj = 
					(SHPMBRInfo *)realloc(pNode->pShapeObj,sizeof(SHPMBRInfo)*pNode->nShpCount);
			}

			SHPMBRInfo pShpInfo;
			pShpInfo.Box = itemRect;
			pShpInfo.nID = key;
			memcpy(pNode->pShapeObj+pNode->nShpCount-1,&pShpInfo,sizeof(SHPMBRInfo));
		}
	}

	//如果是叶子节点,直接放进去
	else if (IsQuadLeaf(pNode))
	{
		if (pNode->nShpCount == 0)	 //如果之前没有节点
		{
			pNode->nShpCount += 1;
			pNode->pShapeObj = 
				(SHPMBRInfo*)malloc(sizeof(SHPMBRInfo)*pNode->nShpCount);
		}
		else
		{
			pNode->nShpCount += 1;
			pNode->pShapeObj = 
				(SHPMBRInfo *)realloc(pNode->pShapeObj,sizeof(SHPMBRInfo)*pNode->nShpCount);
		}

		SHPMBRInfo pShpInfo;
		pShpInfo.Box = itemRect;
		pShpInfo.nID = key;
		memcpy(pNode->pShapeObj+pNode->nShpCount-1,&pShpInfo,sizeof(SHPMBRInfo));
	}
}

void InsertQuad2(long key,MapRect &itemRect,QuadNode* pNode)
{
	QuadNode *node = pNode;		//保留根节点副本
	SHPMBRInfo pShpInfo;

	//节点有孩子
	if (0 < node->nChildCount)
	{
		for (int i = 0; i < 4; i ++)
		{  
			//如果包含或相交,则将节点插入到此节点
			if (node->children[i]->Box.Contains(itemRect)
				|| node->children[i]->Box.Intersects(itemRect))
			{
				//node = node->children[i];
				Insert(key,itemRect,node->children[i]);
			}
		}
	}

	//如果当前节点存在一个子节点时
	else if (0 == node->nChildCount)
	{
		MapRect boxs[4];
		node->Box.Split(boxs,boxs+1,boxs+2,boxs+3);

		int cnt = -1;
		for (int i = 0; i < 4; i ++)
		{  
			//如果包含或相交,则将节点插入到此节点
			if (boxs[i].Contains(itemRect))
			{
				cnt = i;
			}
		}

		//如果有一个矩形包含此对象,则创建四个孩子节点
		if (cnt > -1)
		{
			for (int i = 0; i < 4; i ++)
			{
				//创建四个节点并插入相应的MBR
				node->children[i] = InitQuadNode();
				node->children[i]->Box = boxs[i];
			}
			node->nChildCount = 4;
			InsertQuad2(key,itemRect,node->children[cnt]);	//递归
		}

		//如果都不包含,则直接将对象插入此节点
		if (cnt == -1)
		{
			if (node->nShpCount == 0)	 //如果之前没有节点
			{
				node->nShpCount += 1;
				node->pShapeObj = 
					(SHPMBRInfo*)malloc(sizeof(SHPMBRInfo)*node->nShpCount);
			}
			else if	(node->nShpCount > 0)
			{
				node->nShpCount += 1;
				node->pShapeObj = 
					(SHPMBRInfo *)realloc(node->pShapeObj,
					sizeof(SHPMBRInfo)*node->nShpCount);
			}

			pShpInfo.Box = itemRect;
			pShpInfo.nID = key;
			memcpy(node->pShapeObj,
				&pShpInfo,sizeof(SHPMBRInfo));
		}
	}

	//当前节点没有空间对象
	/*else if (0 == node->nShpCount)
	{
		node->nShpCount += 1;
		node->pShapeObj = 
			(SHPMBRInfo*)malloc(sizeof(SHPMBRInfo)*node->nShpCount);

		pShpInfo.Box = itemRect;
		pShpInfo.nID = key;
		memcpy(node->pShapeObj,&pShpInfo,sizeof(SHPMBRInfo));
	}*/
}

bool IsQuadLeaf(QuadNode* node)
{
	if (NULL == node)
	{
		return 1;
	}
	for (int i = 0; i < 4; i ++)
	{
		if (node->children[i] != NULL)
		{
			return 0;
		}
	}

	return 1;
}

bool DelFalseNode(QuadNode* node)
{
	//如果没有子节点且没有要素
	if (node->nChildCount ==0 && node->nShpCount == 0)
	{
		ReleaseQuadTree(&node);
	}

	//如果有子节点
	else if (node->nChildCount > 0)
	{
		for (int i = 0; i < 4; i ++)
		{
			DelFalseNode(node->children[i]);
		}
	}

	return 1;
}

void TraversalQuadTree(QuadNode* quadTree,vector<int>& resVec)
{
	QuadNode *node = quadTree;
	int i = 0; 
	if (NULL != node)
	{
		//将本节点中的空间对象存储数组中
		for (i = 0; i < node->nShpCount; i ++)
		{
			resVec.push_back((node->pShapeObj+i)->nID);
		}

		//遍历孩子节点
		for (i = 0; i < node->nChildCount; i ++)
		{
			if (node->children[i] != NULL)
			{
				TraversalQuadTree(node->children[i],resVec);
			}
		}
	}

}

void TraversalQuadTree(QuadNode* quadTree,vector<QuadNode*>& arrNode)
{
	deque<QuadNode*> nodeQueue;
	if (quadTree != NULL)
	{
		nodeQueue.push_back(quadTree);
		while (!nodeQueue.empty())
		{
			QuadNode* queueHead = nodeQueue.at(0);	//取队列头结点
			arrNode.push_back(queueHead);
			nodeQueue.pop_front();
			for (int i = 0; i < 4; i ++)
			{
				if (queueHead->children[i] != NULL)
				{
					nodeQueue.push_back(queueHead->children[i]);
				}
			}
		}
	}
}

void ReleaseQuadTree(QuadNode** quadTree)
{
	int i = 0;
	QuadNode* node = *quadTree;
	if (NULL == node)
	{
		return;
	}

	else
	{
		for (i = 0; i < 4; i ++)
		{ 
			ReleaseQuadTree(&node->children[i]);
		}
		free(node);
		node = NULL;
	}

	node = NULL;
}

long CalByteQuadTree(QuadNode* quadTree,long& nSize)
{
	if (quadTree != NULL)
	{
		nSize += sizeof(QuadNode)+quadTree->nChildCount*sizeof(SHPMBRInfo);
		for (int i = 0; i < 4; i ++)
		{
			if (quadTree->children[i] != NULL)
			{
				nSize += CalByteQuadTree(quadTree->children[i],nSize);
			}
		}
	}

	return 1;
}


代码有点长,有需要的朋友可以借鉴并自己优化。

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

四叉树空间索引原理及其实现 的相关文章

  • vnc访问被拒绝怎么办,vnc访问被拒绝怎么办,为什么会被拒绝?

    vnc远程控制连接被拒绝的原因 xff0c 服务器作为网站建设的常用设备 xff0c 在服务器运行过程中起到举足轻重的作用 用户在选择服务器是常用的方式有服务器租用 虚拟主机租用以及服务器托管 xff0c 通过进行文件以及数据的下载 上传等
  • vnc使用图文教程,vnc使用图文教程(含图片教程)

    vnc使用图文教程不知道大家找到过没有 xff0c 毕竟在网上这种教程是很少的 xff0c 因为使用的人都是一些经常使用的 xff0c 但是对于小编这种基础能力差的 xff0c 还是需要vnc使用图文教程的 xff0c 所以小编也是努力了很
  • python中的MapReduce函数和过程浅析

    map reduce思想是Google的JeffDean在2008年在论文 MapReduce Simplified Data Processing on Large Clusters 中提出的 xff0c 而python中沿用了这种思想并
  • openMP学习笔记之一 -- 杂记

    1 使用libffi启动执行 xff0c ffi全称Foreign Function Interface xff0c 参考https www cnblogs com findumars p 4882620 html的介绍 xff0c 2 在
  • Vue系列之—Vue-router详解

    目录 一 简介 1 1 单页面应用 1 2 路由管理器 二 起步 2 1 动态路由匹配 2 2 路由组件传参 2 3 嵌套路由 声明式 编程式导航 命名路由 命名视图 重定向和别名 三 进阶 导航守卫 全局的守卫 路由独享的守卫 一 简介
  • vscode 代码格式化及快捷键

    VSCode 代码格式化 快捷键 On Windows Shift 43 Alt 43 F On Mac Shift 43 Option 43 F On Ubuntu Ctrl 43 Shift 43 I 代码格式化 vscode默认启用了
  • ORB-SLAM2添加稠密建图线程

    注 xff1a 本篇文章只是对高翔博士稠密点云代码进行的简述 xff0c 内容主要包括的是在ORB SLAM2基础上怎么添加稠密建图线程 xff0c 并未对高翔博士代码进行改动 本文章仅用于自己学习记录 xff0c 若有侵权麻烦私聊联系删除
  • ubuntu挂载sd卡到分区目录+修改docker镜像存储位置

    ubuntu挂载sd卡到分区目录 43 修改docker镜像存储位置 一 挂载SD卡到 data 1 查看Linux硬盘信息 lsblk 或 fdisk l lsblk 新的硬盘 xff0c 最好删除之前的分区 xff0c 再新建分区 de
  • Ubuntu虚拟机安装EDA工具:VCS+Verdi+dve2018方法教程

    上个月刚完成Ubuntu虚拟机的安装 xff0c 本教程的基础是你已经安装好了Ubuntu的虚拟机 xff0c 最好是和笔者版本接近的Ubuntu xff0c 具体安装方法已在之前的文章中介绍过了 xff1a https blog csdn
  • 基于deepstream-test3添加跟踪插件和4类sinkType输出(包括rtsp)

    基于deepstream test3添加目标跟踪插件和4类sinkType输出 xff08 包括rtsp输入输出 xff09 deepstream C C 43 43 deepstream官方示例没有给出一个单管道的多类输入和4类sinkT
  • 国外学位论文查找

    转自 xff1a http blog chinaunix net uid 20517852 id 1936377 html 国外博士论文下载的网站 xff0c 不知道以前发过没有 http search ohiolink edu etd i
  • DirectShow播放器(LAVFilter + EVR)开发例子

    LAVFilter是一套著名的DirectShow插件 xff0c 包括Demux xff0c Video Decoder xff0c AudioDecoder xff0c 播放文件所需要的几个重要插件都包含进去了 xff0c 并且支持播放
  • 关于最新版的keil5不能正常调试或者调试过程自动停止的解决方法

    适用范围 1 在进入debug的功能中提示J link is defective xff0c 大概意思就是最新版的J LINK驱动跟正在用的硬件不匹配 xff0c 要你更换驱动或者更换硬件 xff08 其实是使用盗版的J LINK会出现的问
  • C++11变参模板的参数包

    Parameter pack 原文来自这里 A template parameter pack is a template parameter that accepts zero or more template arguments non
  • Linux socket 关闭场景

    测试环境 root 64 centos192 168 1 12 cat etc system release CentOS release 6 9 Final 工具 xff1a 服务器 192 168 1 12 ipython Python
  • QGroundControl地面站二次开发环境搭建(win+linux+android)

    更新时间 xff1a 2017 6 19 大家好 xff0c 我是learn xff0c 下面主要介绍一下QGroundControl地面站的环境搭建 网上也有好多教程 xff0c 我就不再麻烦了 xff0c 补充一下好了 http blo
  • std::vector用法

    vector 是C 43 43 标准模板库中的部分内容 xff0c 它是一个多功能的 xff0c 能够操作多种数据结构和算法的模板类和函数库 vector之所以被认为是一个容器 xff0c 是因为它能够像容器一样存放各种类型的对象 xff0
  • Linux串口(serial、uart)驱动程序设计

    一 核心数据结构 串口驱动有3个核心数据结构 xff0c 它们都定义在 lt include linux serial core h gt 1 uart driver uart driver包含了串口设备名 串口驱动名 主次设备号 串口控制
  • Xshell 5 评估过期,需要采购,不能使用的解决办法

    Xshell 5 当然 xff0c 现在我们可以直接撸 Xshell 6 了 卸载原来的 Xshell 5进入 Xshell 5 官网 xff1a https www netsarang com页面上点导航栏的 Free Licence x
  • mapreduce编程(一)-二次排序

    mr自带的例子中的源码SecondarySort xff0c 我重新写了一下 xff0c 基本没变 这个例子中定义的map和reduce如下 xff0c 关键是它对输入输出类型的定义 xff1a xff08 java泛型编程 xff09 p

随机推荐

  • Android apk执行shell脚本 工具类

    在做Android应用时 xff0c 经常需要执行shell脚本 xff0c 以快速实现某些功能 xff1b 在Android应用程序中执行shell脚本可以省去一大堆繁琐的代码 xff0c 还可以避免不必要的错误 xff1b 比如 xff
  • Python最强装逼神技!微信远程控制电脑,想让你电脑关机就关机!

    今天带给大家一个非常有意思的 python 程序 xff0c 基于 itchat 实现微信控制电脑 你可以通过在微信发送命令 xff0c 来拍摄当前电脑的使用者 xff0c 然后图片会发送到你的微信上 甚至你可以发送命令来远程关闭电脑 程序
  • 基于ESKF的IMU姿态融合【附MATLAB代码】

    目录 0 前言1 什么是ESKF2 系统方程2 1 状态变量2 2 imu的测量值2 3 预测方程及雅克比矩阵2 4 测量方程及雅克比矩阵 3 kalman filter loop计算4 Show me the code5 代码下载链接 0
  • 【笔记】自适应卡尔曼滤波 Adaptive Extended Kalman Filter

    0 阅读文章 Adaptive Adjustment of Noise Covariance in Kalman Filter for Dynamic State Estimation 1 主要内容 一般情况下 xff0c kalman中的
  • 二、Docker:Dockerfile的使用、指令详解和示例

    什么是 Dockerfile xff1f Dockerfile 是一个用来构建镜像的文本文件 xff0c 文本内容包含了一条条构建镜像所需的指令和说明 使用 Dockerfile 定制镜像 1 使用 dockerfile 定制 nginx
  • # STM32中断方式实现串口通信(标准库)

    STM32中断方式实现串口通信 xff08 标准库 xff09 文章目录 STM32中断方式实现串口通信 xff08 标准库 xff09 一 串口通信原理以及中断原理一 问题分析1 涉及外设2 状态机实现 二 创建MDK xff08 kei
  • 一张图看懂阿里云网络产品[一]网络产品概览

    一张图看懂网络产品系列文章 xff0c 让用户用最少的时间了解网络产品 xff0c 本文章是第一篇 网络产品概览 系列文章持续更新中 xff0c 敬请关注 xff3b 一 xff3d 网络产品概览 xff3b 二 xff3d VPC xff
  • MapReduce原理及编程实现

    文章目录 MapReduce原理及编程实现MapReduce基本概念MapReduce执行过程Mapper阶段Reducer阶段Combiner类Partitioner类 MapReduce实现WordCountKey amp Value类
  • 2020-10-20 学习日志(Crazepony控制环)

    2020年10月20日 学习任务 xff1a 完成Crazepony控制环的理解 之前是通过姿态解算获得了 四元数 旋转矩阵 欧拉角 CtrlAttiRate void CtrlAttiRate void float yawRateTarg
  • STL学习笔记之迭代器--iterator

    STL设计的精髓在于 xff0c 把容器 xff08 Containers xff09 和算法 xff08 Algorithms xff09 分开 xff0c 彼此独立设计 xff0c 最后再用迭代器 xff08 Iterator xff0
  • 提升工作效率之PCB设计软件“立创EDA”

    文章目录 前言一 立创EDA二 PCB生产三 团队功能总结 前言 由于工作需要设计一款硬件调试小工具 xff0c 考虑到器件采购和PCB制版都在立创商城上进行 xff0c 索性就试用立创EDA进行PCB设计 结论在前 xff1a 立创线上E
  • nvidia显卡,驱动以及cuda版本对应查询

    实验室新买了一块rtx 2080和titan rtx xff0c 需要分别配置驱动和cuda xff0c 但是一直也找不到显卡和cuda的官方对照表 xff0c 每次都是百度 谷歌 必应 xff0c 参考别人安装之旅 今天突然发现了驱动和c
  • LoRa 信噪比和接收灵敏度

    文章目录 前言一 信噪比极限 xff08 SNR LIMIT xff09 二 接收灵敏度 前言 介绍信噪比极限和如何计算接收灵敏度 参考资料 xff1a LoRa信噪比和接收灵敏度 一 信噪比极限 xff08 SNR LIMIT xff09
  • C在字符串后面加/0和0

    使用复制字符串时 xff0c 经常会遇到字符串后面跟着一大堆莫名其妙的字符串 xff0c 例如屯屯屯 之类的东西 xff0c 这是因为在使用字符串时没有在字符串结尾加 0或0 通常分配一块内存到堆上或栈上时 xff0c 内存区域可能会有之前
  • 基于k8s+prometheus实现双vip可监控Web高可用集群

    目录 一 规划整个项目的拓扑结构和项目的思维导图 二 修改好各个主机的主机名 xff0c 并配置好每台机器的ip地址 网关和dns等 2 1修改每个主机的ip地址和主机名 2 2 关闭firewalld和selinux 三 使用k8s实现W
  • PX4源码开发人员文档(一)——软件架构

    软件架构 PX4 在广播消息网络内 xff0c 按照一组节点 xff08 nodes xff09 的形式进行组织 xff0c 网络之间使用像如 姿态 和 位置 之类的语义通道来传递系统状态 软件的堆栈结构主要分为四层 应用程序接口 提供给a
  • ardupilot线程理解

    对于apm和pixhawk一直存在疑惑 xff0c 到现在还不是特别清楚 今天在http dev ardupilot com 看到下面的说明 xff0c 感觉很有用 xff0c 对于整体理解amp代码很有帮助 xff0c 所以记下来 转载请
  • Pixhawk源码笔记三:串行接口UART和Console

    这里 xff0c 我们对 APM UART Console 接口进行讲解 如有问题 xff0c 可以交流30175224 64 qq com 新浪 64 WalkAnt xff0c 转载本博客文章 xff0c 请注明出处 xff0c 以便更
  • C/C++中二维数组和指针关系分析

    在C c 43 43 中 xff0c 数组和指针有着密切的关系 xff0c 有很多地方说数组就是指针式错误的一种说法 这两者是不同的数据结构 其实 xff0c 在C c 43 43 中没有所谓的二维数组 xff0c 书面表达就是数组的数组
  • 四叉树空间索引原理及其实现

    今天依然在放假中 xff0c 在此将以前在学校写的四叉树的东西拿出来和大家分享 四叉树索引的基本思想是将地理空间递归划分为不同层次的树结构 它将已知范围的空间等分成四个相等的子空间 xff0c 如此递归下去 xff0c 直至树的层次达到一定