好久好久没更过了,因为入坑了一本天书的缘故,有一些比较特别的数据结构需要学一下,第一个就是这个四叉树,工作占的时间太多了,只能用剩下的时间来学一学了,话不多说,先来介绍一下基本的概念:
我们这里的四叉树指的是满四叉树,也就是每个节点要么有四个孩子节点,要么没有孩子节点,它在二维空间中可以表示对一个正方形进行四个象限的分割,从右上,左上,左下,右下这四个方向可以依次表示为第一象限,第二象限,第三象限和第四象限,如图:其中,根节点表示大的正方形,根节点下的四个节点分别表示对应的四个分割出来的正方形。
这里,还有三个关于四叉树的定理:
1.平面中一组点P的四叉树的深度最多为log(s/c) + 3/2,其中c是P中任意两个点之间的最小距离,s是正方形的边长。
2.存储了一组n个点,深度为d的四叉树,具有O(d + 1)个结点并且可以在O((d+1)n)时间内构造。
3.设T为深度为d的四叉树,如上所述,T中给定节点v在给定方向上的邻居可以在O(d+1)时间内找到。
定理为啥成立,我也不知道,一本很阴间的书上写的。 定义和定理结束了,下面是代码,我用C++实现的:
关于四叉树的类定义:(重载的运算符和构造函数,析构函数,拷贝构造函数方法体都放在类里了,自定义的函数方法体放在了类外)
对于四叉树的每个节点,我定义它有一个矩形区域,有一个链表pointList表示在这个矩形区域内的点,以及四个指向孩子节点的指针和指向父节点的指针,还有一系列函数。书上虽然是按正方形讲的,但是,矩形好像更加准确,所以我下边都写的矩形。
class QuadTree
{
public:
//四叉树对应的矩形区域。
Rectangle treeRec;
QuadTree(){}
QuadTree(Rectangle treeRec){this->treeRec = treeRec;}
QuadTree(Rectangle treeRec,PointList* pointList)
{
this->treeRec = treeRec;
this->pointList = pointList;
}
//对于用这个构造函数的形式创建的四叉树,是在矩形区域分裂得到子节点的时候用到的,因此,对于父节点矩形区域中的点链表(不带头节点),需要分给子节点。
QuadTree(Rectangle treeRec,QuadTree* parent)
{
this->treeRec = treeRec;
this->pointList = pointList;
this->parent = parent;
PointList* parentList = parent->pointList;
pointList = new PointList();
Point p(-1,-1);
pointList->point = p;
//cout<
while(parentList!=NULL)
{
if(treeRec.IsInRectangle(parentList->point))
{
if(pointList->point.x == -1)
{
pointList->point = parentList->point;
//cout<<1<
else
{
pointList->Insert(parentList->point);
}
}
parentList = parentList->next;
}
}
~QuadTree()
{
delete rightTopTree;
delete leftTopTree;
delete leftBottomTree;
delete rightBottomTree;
delete pointList;
delete this;
}
QuadTree* rightTopTree;
QuadTree* leftTopTree;
QuadTree* leftBottomTree;
QuadTree* rightBottomTree;
QuadTree* parent;
//这个表示一个点的链表,表示这个父节点的矩形区域中有这些点。
PointList* pointList;
//剩下的函数我会在代码中说明,读者读到这只需要根据它的名字明白它是干啥的就行了。
void CreateChildren();
Rectangle* RectangleSearch(Point point,Rectangle* rec,QuadTree* root);
bool PointSearch(Point point);
bool PointDelete(Point point,QuadTree* tar);
bool RectangleDelete(Rectangle rec,QuadTree* tar);
void DeleteAllChildren(QuadTree* tar);
QuadTree* FindNeibor(Direction dir,QuadTree* tar,QuadTree* parent);
};
下面是矩形和点,以及点链表的定义,没啥好说的:
class Point
{
public:
int x;
int y;
Point(const Point &point)
{
x = point.x;
y = point.y;
}
Point(int x,int y)
{
this->x = x;
this->y = y;
}
Point()
{
}
~Point(){}
bool operator==(Point& other)
{
return this->x == other.x && this->y == other.y;
}
bool operator!=(Point& other)
{
return this->x != other.x || this->y != other.y;
}
Point operator=(Point& other)
{
this->x = other.x;
this->y = other.y;
return *this;
}
};
class PointList
{
public:
Point point;
PointList* next;
void Insert(Point point);
friend bool Delete(PointList*& tar, Point point);
//Fix表示的是改
void Fix(Point point,Point tar);
bool Search(Point point);
};
class Rectangle
{
public:
int xMin;
int xMax;
int yMin;
int yMax;
Rectangle(int xMin,int xMax,int yMin,int yMax)
{
this->xMin = xMin;
this->xMax = xMax;
this->yMin = yMin;
this->yMax = yMax;
}
bool operator==(const Rectangle& other)
{
return this->xMin == other.xMin && this->xMax == other.xMax && this->yMin == other.yMin && this->yMax == other.yMax;
}
bool operator!=(const Rectangle& other)
{
return !(this->xMin == other.xMin && this->xMax == other.xMax && this->yMin == other.yMin && this->yMax == other.yMax);
}
friend ostream& operator<<(ostream& output,Rectangle rec);
bool IsInRectangle(Point tar);
bool Contains(Rectangle tar);
Rectangle* Split();
Rectangle(){}
~Rectangle(){}
};
这里我主函数这样写是为了构建这样一个矩形区域和点集:
int main()
{
Rectangle* rootRec = new Rectangle(0,16,0,16);
QuadTree* root = new QuadTree(*rootRec);
root->pointList = new PointList();
Point rootPoint(1,2);
root->pointList->point = rootPoint;
root->parent = NULL;
root->pointList->next = NULL;
root->pointList->Insert(Point(2,9));
root->pointList->Insert(Point(2,14));
root->pointList->Insert(Point(5,9));
root->pointList->Insert(Point(5,11));
root->pointList->Insert(Point(6,14));
root->pointList->Insert(Point(7,9));
root->pointList->Insert(Point(7,11));
root->pointList->Insert(Point(9,6));
root->pointList->Insert(Point(9,12));
root->CreateChildren();
root->leftTopTree->CreateChildren();
root->leftTopTree->rightBottomTree->CreateChildren();
root->leftTopTree->rightTopTree->CreateChildren();
Rectangle* test = new Rectangle();
test->xMin = 4;
test->xMax = 6;
test->yMin = 8;
test->yMax = 10;
cout<
return 0;
}
下边是QuadTree中的函数的详解:
这个是最重要的一个函数:邻居搜索,顾名思义,就是找一个矩形的邻居,它是这样定义的:找一个节点v的北方邻居:如果这个节点v没有父节点,则它没有北方邻居。如果它有父节点,如果它是父节点的左下或者右下孩子,则它的北方邻居为它正上方与它同一层的节点;如果它是父节点下左上或者右上孩子,则需要按如下方式递归:递归的找到它父级节点的北方邻居μ,如果μ有子节点,则它的北方邻居是μ的子节点,否则为μ本身。
其他方向的邻居同理。
QuadTree* QuadTree::FindNeibor(Direction dir,QuadTree* tar,QuadTree* parent)
{
switch(dir)
{
if(parent == NULL)
return NULL;
QuadTree* parentNeibor;
//这里就以北方邻居为例了
case North:
//如果说当前这个要被找邻居的节点v是它父节点的左下或者右下节点,则找到它上边和他挨着的兄弟节点即可
if(tar == parent->leftBottomTree)
return parent->leftTopTree;
if(tar == parent->rightBottomTree)
return parent->rightTopTree;
//否则,找到它父节点的北方邻居
parentNeibor = FindNeibor(North,parent,parent->parent);
//如果说父节点没有孩子节点 ,则父节点的北方邻居就是v的北方邻居
if(parentNeibor->rightTopTree == NULL)
return parentNeibor;
//否则,就找到父节点的北方邻居下和v同层的节点作为v的北方邻居
if(tar == parent->leftTopTree)
return parentNeibor->leftBottomTree;
return parentNeibor->rightBottomTree;
break;
case South:
if(tar == parent->leftTopTree)
return parent->leftBottomTree;
if(tar == parent->rightTopTree)
return parent->rightBottomTree;
parentNeibor = FindNeibor(South,parent,parent->parent);
if(parentNeibor->rightTopTree == NULL)
return parentNeibor;
if(tar == parent->leftBottomTree)
return parentNeibor->leftTopTree;
return parentNeibor->rightTopTree;
break;
case East:
if(tar == parent->leftTopTree)
return parent->rightTopTree;
if(tar == parent->leftBottomTree)
return parent->rightBottomTree;
parentNeibor = FindNeibor(East,parent,parent->parent);
if(tar == parent->rightTopTree)
return parentNeibor->leftTopTree;
return parentNeibor->leftBottomTree;
break;
case West:
if(tar == parent->rightTopTree)
return parent->leftTopTree;
if(tar == parent->rightBottomTree)
return parent->leftBottomTree;
parentNeibor = FindNeibor(West,parent,parent->parent);
if(tar == parent->leftTopTree)
return parentNeibor->rightTopTree;
return parentNeibor->rightBottomTree;
break;
}
return NULL;
}
//这个地方之所以要将重写的<<运算符作为矩形类的友元,是为了让矩形类作为<<的右操作数,即cout< ostream& operator<<(ostream& output,Rectangle rec) //这个地方点删除的思路是:对于要删除点的树tar,要将它的点链表中与point相同的点删除,还要将tar的孩子节点的点链表中与point相同的点相同, 因为同一个点父节点有,肯定有一个孩子节点有这个点 //这个没什么好说的 void QuadTree::DeleteAllChildren(QuadTree* tar) //删除树中的一个矩形,还要将它的兄弟节点一并删除 bool QuadTree::RectangleDelete(Rectangle rec,QuadTree* tar) //判断一个点是否在矩形中,不考虑边界 bool Rectangle::IsInRectangle(Point tar) //判断一个矩形中是否有另一个矩形,考虑边界 bool Rectangle::Contains(Rectangle tar) //点查询,就是从点链表中找对应的点 bool QuadTree::PointSearch(Point point) //矩形查询,这个也是很重要的一个函数,四叉树之所以有用,这上边有很大分量,对于一个查找一个点是不是有,在哪个矩形里,直接用点查询,时间复杂度是O(n),而用这个相当于是进行了四分查找,需要找的层数是log以4为底,n的一次方多项式的对数,即O(logn)级别的。 对于每一个点,如果说一个矩形里包含它,就去这个矩形里找,一直找到叶节点,即为这个点所在的矩形。 Rectangle* QuadTree::RectangleSearch(Point point,Rectangle* rec,QuadTree* root) //链表的查询 bool PointList::Search(Point point) //链表的修改 void PointList::Fix(Point point,Point tar) //链表增加节点 //链表删除节点,之所以穿了指针的引用,是因为不带头节点,如果要删除的节点是头节点,直接操作形参里的指针tar,tar和this指向的那块内存上的内容虽然变了,但原来的this指针是不会往后挪的(这样就会有问题),而this指针又不能作为左操作数,所以整了一个指针的引用。 bool Delete(PointList*& tar,Point point) //矩形的分裂函数,将一个矩形分成四个小矩形,按照长和宽的中点来分。 Rectangle* Rectangle::Split() //创建孩子节点,每个孩子节点需要得到赋值后的原父节点的矩形分裂后的引用和父节点的引用 void QuadTree::CreateChildren() 完整的代码如下:
{
output<
}
bool QuadTree::PointDelete(Point point,QuadTree* tar)
{
bool deleteSuccess = Delete(tar->pointList,point);
if(!deleteSuccess)
return false;
if(!tar->rightTopTree)
return true;
if(tar->rightTopTree->treeRec.IsInRectangle(point))
{
return PointDelete(point,tar->rightTopTree);
}
if(tar->leftTopTree->treeRec.IsInRectangle(point))
{
return PointDelete(point,tar->leftTopTree);
}
if(tar->leftBottomTree->treeRec.IsInRectangle(point))
{
return PointDelete(point,tar->leftBottomTree);
}
if(tar->rightBottomTree->treeRec.IsInRectangle(point))
{
return PointDelete(point,tar->rightBottomTree);
}
}
{
QuadTree* rightTop = tar->rightTopTree;
QuadTree* leftTop = tar->leftTopTree;
QuadTree* leftBottom = tar->leftBottomTree;
QuadTree* rightBottom = tar->rightBottomTree;
tar->rightTopTree = NULL;
tar->leftTopTree = NULL;
tar->leftBottomTree = NULL;
tar->rightBottomTree = NULL;
}
{
if(!tar->treeRec.Contains(rec))
{
return false;
}
if(tar->rightTopTree&&tar->rightTopTree->treeRec.Contains(rec))
{
if(tar->rightTopTree->treeRec == rec)
{
DeleteAllChildren(tar);
return true;
}
return RectangleDelete(rec,tar->rightTopTree);
}
if(tar->leftTopTree&&tar->leftTopTree->treeRec.Contains(rec))
{
if(tar->leftTopTree->treeRec == rec)
{
DeleteAllChildren(tar);
return true;
}
return RectangleDelete(rec,tar->leftTopTree);
}
if(tar->leftBottomTree&&tar->leftBottomTree->treeRec.Contains(rec))
{
if(tar->leftBottomTree->treeRec == rec)
{
DeleteAllChildren(tar);
return true;
}
return RectangleDelete(rec,tar->leftBottomTree);
}
if(tar->rightBottomTree&&tar->rightBottomTree->treeRec.Contains(rec))
{
if(tar->rightBottomTree->treeRec == rec)
{
DeleteAllChildren(tar);
return true;
}
return RectangleDelete(rec,tar->rightBottomTree);
}
}
{
return tar.x > xMin && tar.x < xMax && tar.y > yMin && tar.y < yMax;
}
{
return this->xMin <= tar.xMin && this->xMax >= tar.xMax && this->yMin <= tar.yMin && this->yMax >= tar.yMax;
}
{
return this->pointList->Search(point);
}
{
if(!rec->IsInRectangle(point))
{
return NULL;
}
if(root->rightTopTree&&root->rightTopTree->treeRec.IsInRectangle(point))
{
return QuadTree::RectangleSearch(point,&root->rightTopTree->treeRec,root->rightTopTree);
}
if(root->leftTopTree&&root->leftTopTree->treeRec.IsInRectangle(point))
{
return QuadTree::RectangleSearch(point,&root->leftTopTree->treeRec,root->leftTopTree);
}
if(root->leftBottomTree&&root->leftBottomTree->treeRec.IsInRectangle(point))
{
return QuadTree::RectangleSearch(point,&root->leftBottomTree->treeRec,root->leftBottomTree);
}
if(root->rightBottomTree&&root->rightBottomTree->treeRec.IsInRectangle(point))
{
return QuadTree::RectangleSearch(point,&root->rightBottomTree->treeRec,root->rightBottomTree);
}
return rec;
}
{
PointList* hip = this;
while(hip != NULL)
{
if(hip->point == point)
return true;
hip = hip->next;
}
return false;
}
{
PointList* hip = this;
while(hip->next->point != tar)
{
hip = hip->next;
}
hip->next-> point = point;
}
void PointList::Insert(Point point)
{
PointList* hip = this;
while(hip->next)
{
hip = hip->next;
}
PointList* nextPoint = new PointList();
nextPoint->point = point;
nextPoint->next = NULL;
hip->next = nextPoint;
}
{
PointList* hip = tar;
if(hip->point == point)
{
tar = hip->next;
delete hip;
return true;
}
while(hip->next && hip->next->point != point)
{
hip = hip->next;
}
if(!hip->next)
return false;
PointList* pointToBeDeleted = hip->next;
hip->next = hip->next->next;
delete pointToBeDeleted;
return true;
}
{
Rectangle* rec = new Rectangle[4];
rec[0].xMin = (this->xMin + this->xMax)/2;
rec[0].xMax = this->xMax;
rec[0].yMin = (this->yMin + this->yMax)/2;
rec[0].yMax = this->yMax;
rec[1].xMin = this->xMin;
rec[1].xMax = (this->xMin + this->xMax)/2;
rec[1].yMin = (this->yMin + this->yMax)/2;
rec[1].yMax = this->yMax;
rec[2].xMin = this->xMin;
rec[2].xMax = (this->xMin + this->xMax)/2;
rec[2].yMin = this->yMin;
rec[2].yMax = (this->yMin + this->yMax)/2;
rec[3].xMin = (this->xMin + this->xMax)/2;
rec[3].xMax = this->xMax;
rec[3].yMin = this->yMin;
rec[3].yMax = (this->yMin + this->yMax)/2;
return rec;
}
{
Rectangle* tarRecArray = this->treeRec.Split();
rightTopTree = new QuadTree(tarRecArray[0],this);
leftTopTree = new QuadTree(tarRecArray[1],this);
leftBottomTree = new QuadTree(tarRecArray[2],this);
rightBottomTree = new QuadTree(tarRecArray[3],this);
}#include