C语言实现一个四叉树quadtree
cheungmine
用C语言实现一个2维四叉树quadtree,具有一定的实际意义。你可以把几何图形的索引(用long型的id标识)放到这个树中(根据最小边界矩形)。quadtree可以用来快速区域查找图形,虽然不是那么精确,但是毕竟没有漏掉的。虽然quadtree的效率不如RTree?但是RTree的实现毕竟复杂了些,我会尽快收集整理出RTree的代码。RTree确实比QuadTree好的多?(起码RTree很时髦啊!)
头文件如下:
/*
* quadtree.h
* Quad tree structure -- for spatial quick searching
* cheungmine
* Oct. 5, 2007. All rights reserved.
*/
#ifndef QUADTREE_H_INCLUDED
#define QUADTREE_H_INCLUDED
#include " unistd.h "
#include " list.h "
#define QUAD_SUBNODES 4
#define QBOX_OVERLAP_MAX 0.4
#define QBOX_OVERLAP_MIN 0.02
#define QTREE_DEPTH_MAX 8
#define QTREE_DEPTH_MIN 4
#define QUADRANT_BITS 3
/* a quadrant defined below:
NW(1) | NE(0)
-----------|-----------
SW(2) | SE(3)
*/
typedef enum
{
NE = 0 ,
NW = 1 ,
SW = 2 ,
SE = 3
}QuadrantEnum;
/* a box defined below:
_____max
|__|__|
|__|__|
min
*/
typedef struct _quadbox_t
{
double _xmin,
_ymin,
_xmax,
_ymax;
}quadbox_t;
/* quad node */
typedef struct _quadnode_t
{
quadbox_t _box; /* node bound box */
list_t * _lst; /* node data list */
struct _quadnode_t * _sub[QUAD_SUBNODES]; /* pointer to subnodes of this node */
}quadnode_t;
/* quad tree */
typedef struct _quadtree_t
{
quadnode_t * _root;
int _depth; /* max depth of tree: 0-based */
float _overlap; /* overlapped ratio of quanbox */
}quadtree_t;
/* =============================================================================
Public Functions
============================================================================= */
/* creates a quadtree and returns a pointer to it */
extern quadtree_t *
quadtree_create (quadbox_t box,
int depth, /* 4~8 */
float overlap /* 0.02 ~ 0.4 */
);
/* destroys a quad tree and free all memory */
extern void
quadtree_destroy (IN quadtree_t * qtree
);
/* inserts a node identified by node_key into a quadtree, returns the node quadtree encoding */
extern quadnode_t *
quadtree_insert (IN quadtree_t * qtree,
IN long node_key,
IN quadbox_t * node_box
);
/* searches nodes inside search_box */
extern void
quadtree_search (IN const quadtree_t * qtree,
IN quadbox_t * search_box,
OUT list_t * results_list
);
#endif // QUADTREE_H_INCLUDED
* quadtree.h
* Quad tree structure -- for spatial quick searching
* cheungmine
* Oct. 5, 2007. All rights reserved.
*/
#ifndef QUADTREE_H_INCLUDED
#define QUADTREE_H_INCLUDED
#include " unistd.h "
#include " list.h "
#define QUAD_SUBNODES 4
#define QBOX_OVERLAP_MAX 0.4
#define QBOX_OVERLAP_MIN 0.02
#define QTREE_DEPTH_MAX 8
#define QTREE_DEPTH_MIN 4
#define QUADRANT_BITS 3
/* a quadrant defined below:
NW(1) | NE(0)
-----------|-----------
SW(2) | SE(3)
*/
typedef enum
{
NE = 0 ,
NW = 1 ,
SW = 2 ,
SE = 3
}QuadrantEnum;
/* a box defined below:
_____max
|__|__|
|__|__|
min
*/
typedef struct _quadbox_t
{
double _xmin,
_ymin,
_xmax,
_ymax;
}quadbox_t;
/* quad node */
typedef struct _quadnode_t
{
quadbox_t _box; /* node bound box */
list_t * _lst; /* node data list */
struct _quadnode_t * _sub[QUAD_SUBNODES]; /* pointer to subnodes of this node */
}quadnode_t;
/* quad tree */
typedef struct _quadtree_t
{
quadnode_t * _root;
int _depth; /* max depth of tree: 0-based */
float _overlap; /* overlapped ratio of quanbox */
}quadtree_t;
/* =============================================================================
Public Functions
============================================================================= */
/* creates a quadtree and returns a pointer to it */
extern quadtree_t *
quadtree_create (quadbox_t box,
int depth, /* 4~8 */
float overlap /* 0.02 ~ 0.4 */
);
/* destroys a quad tree and free all memory */
extern void
quadtree_destroy (IN quadtree_t * qtree
);
/* inserts a node identified by node_key into a quadtree, returns the node quadtree encoding */
extern quadnode_t *
quadtree_insert (IN quadtree_t * qtree,
IN long node_key,
IN quadbox_t * node_box
);
/* searches nodes inside search_box */
extern void
quadtree_search (IN const quadtree_t * qtree,
IN quadbox_t * search_box,
OUT list_t * results_list
);
#endif // QUADTREE_H_INCLUDED
实现文件如下:
/*
* quadtree.c
* Quad tree implementation -- for spatial quick searching
* cheungmine
* Oct. 5, 2007. All rights reserved.
*/
#include " quadtree.h "
#include < assert.h >
/* =============================================================================
Private Functions
============================================================================= */
static BOOL quadbox_is_valid (quadbox_t * qb)
{
return (qb -> _xmin < qb -> _xmax && qb -> _ymin < qb -> _ymax) ? TRUE : FALSE;
}
static double quadbox_width ( const quadbox_t * qb)
{
return (qb -> _xmax - qb -> _xmin);
}
static double quadbox_height ( const quadbox_t * qb)
{
return (qb -> _ymax - qb -> _ymin);
}
static void quadbox_init (quadbox_t * qb,
double xmin, double ymin, double xmax, double ymax)
{
qb -> _xmin = xmin; qb -> _ymin = ymin; qb -> _xmax = xmax; qb -> _ymax = ymax;
assert (quadbox_is_valid (qb) );
}
static void quadbox_inflate(quadbox_t * qb, double dx, double dy)
{
assert (dx > 0 && dy > 0 );
qb -> _xmin -= (dx / 2 );
qb -> _xmax += (dx / 2 );
qb -> _ymin -= (dy / 2 );
qb -> _ymax += (dy / 2 );
}
/* splits the quadrant such as below:
nw(0010) | ne(0001)
----------|----------
sw(0100) | se(1000)
*/
static void quadbox_split( const quadbox_t * qb,
quadbox_t * ne, quadbox_t * nw, quadbox_t * se, quadbox_t * sw,
float overlap)
{
double dx = quadbox_width(qb) * ( 1.0 + overlap) / 2 ;
double dy = quadbox_height(qb) * ( 1.0 + overlap) / 2 ;
assert (overlap >= QBOX_OVERLAP_MIN - 0.0001 );
assert (overlap <= QBOX_OVERLAP_MAX + 0.0001 );
quadbox_init (ne, qb -> _xmax - dx, qb -> _ymax - dy, qb -> _xmax, qb -> _ymax);
quadbox_init (nw, qb -> _xmin, qb -> _ymax - dy, qb -> _xmin + dx, qb -> _ymax);
quadbox_init (sw, qb -> _xmin, qb -> _ymin, qb -> _xmin + dx, qb -> _ymin + dy);
quadbox_init (se, qb -> _xmax - dx, qb -> _ymin, qb -> _xmax, qb -> _ymin + dy);
}
/* returns TRUE if the first is inside the senond */
static BOOL quadbox_is_inside( const quadbox_t * _first, const quadbox_t * _second)
{
return (_second -> _xmin < _first -> _xmin && _second -> _xmax > _first -> _xmax &&
_second -> _ymin < _first -> _ymin && _second -> _ymax > _first -> _ymax) ? TRUE : FALSE;
}
/* returns TRUE if two quad_box is overlapped */
static BOOL quadbox_is_overlapped( const quadbox_t * _first, const quadbox_t * _second)
{
return (_first -> _xmin > _second -> _xmax || _first -> _xmax < _second -> _xmin ||
_first -> _ymin > _second -> _ymax || _first -> _ymax < _second -> _ymin) ? FALSE : TRUE;
}
static quadnode_t * quadnode_create ( const quadbox_t * box)
{
quadnode_t * node = (quadnode_t * ) calloc ( 1 , sizeof (quadnode_t));
if (node)
memcpy ( & (node -> _box), box, sizeof (quadbox_t));
return node;
}
static void quadnode_destroy(quadnode_t * node)
{
if (node -> _sub[NE] != 0 ){
quadnode_destroy (node -> _sub[NE]);
quadnode_destroy (node -> _sub[NW]);
quadnode_destroy (node -> _sub[SE]);
quadnode_destroy (node -> _sub[SW]);
}
if (node -> _lst)
list_destroy(node -> _lst, NULL);
free (node);
}
static void quadnode_create_child(quadnode_t * node, float overlap, int depth)
{
quadbox_t ne, nw, se, sw;
assert (node);
quadbox_split ( & (node -> _box), & ne, & nw, & se, & sw, overlap );
node -> _sub[NE] = quadnode_create ( & ne);
node -> _sub[NW] = quadnode_create ( & nw);
node -> _sub[SW] = quadnode_create ( & sw);
node -> _sub[SE] = quadnode_create ( & se);
}
static BOOL quadnode_has_child( const quadnode_t * node)
{
return (node -> _sub[NE] != 0 );
}
static BOOL quadnode_has_data ( const quadnode_t * node)
{
return (node -> _lst && node -> _lst -> size > 0 ) ? TRUE: FALSE;
}
static void quadnode_add_data ( quadnode_t * node, long node_key )
{
assert (node);
if ( ! node -> _lst) node -> _lst = list_create();
assert (node -> _lst);
list_append_node (node -> _lst, list_key_create (node_key));
}
/* inserts a node to parent node of tree. returns pointer to node */
static quadnode_t * quadtree_insert_node ( quadtree_t * tree,
quadnode_t * parent,
long node_key,
quadbox_t * node_box,
int * depth
)
{
if ( quadbox_is_inside (node_box, & (parent -> _box)) )
{
if ( ++ ( * depth) < tree -> _depth )
{
if ( ! quadnode_has_child ( parent ) )
quadnode_create_child (parent, tree -> _overlap, ( * depth));
if ( quadbox_is_inside (node_box, & (parent -> _sub[NE] -> _box) ) )
return quadtree_insert_node (tree, parent -> _sub[NE], node_key, node_box, depth);
if ( quadbox_is_inside (node_box, & (parent -> _sub[NW] -> _box) ) )
return quadtree_insert_node (tree, parent -> _sub[NW], node_key, node_box, depth);
if ( quadbox_is_inside (node_box, & (parent -> _sub[SW] -> _box) ) )
return quadtree_insert_node (tree, parent -> _sub[SW], node_key, node_box, depth);
if ( quadbox_is_inside (node_box, & (parent -> _sub[SE] -> _box) ) )
return quadtree_insert_node (tree, parent -> _sub[SE], node_key, node_box, depth);
}
/* inserts into this node since it can NOT be included in any subnodes */
quadnode_add_data (parent, node_key);
return parent;
}
return NULL;
}
/* searched tree nodes */
static void quadtree_search_nodes (quadnode_t * current_node,
quadbox_t * search_box,
list_t * results_list
)
{
if ( quadbox_is_overlapped ( & (current_node -> _box), search_box ) )
{
if ( quadnode_has_data (current_node) )
list_append_node (results_list, list_node_create(current_node));
if ( quadnode_has_child (current_node) )
{
quadtree_search_nodes (current_node -> _sub[NE], search_box, results_list);
quadtree_search_nodes (current_node -> _sub[NW], search_box, results_list);
quadtree_search_nodes (current_node -> _sub[SW], search_box, results_list);
quadtree_search_nodes (current_node -> _sub[SE], search_box, results_list);
}
}
}
/* =============================================================================
Public Functions
============================================================================= */
quadtree_t *
quadtree_create (quadbox_t box,
int depth, /* 4~8 */
float overlap /* 0.02 ~ 0.4 */
)
{
quadtree_t * qt = NULL;
assert (depth >= QTREE_DEPTH_MIN - 0.0001 && depth <= QTREE_DEPTH_MAX + 0.0001 );
assert (overlap >= QBOX_OVERLAP_MIN - 0.0001 && overlap <= QBOX_OVERLAP_MAX + 0.0001 );
qt = (quadtree_t * ) calloc ( 1 , sizeof (quadtree_t));
if (qt)
{
qt -> _depth = depth;
qt -> _overlap = overlap;
quadbox_inflate ( & box, quadbox_width( & box) * overlap, quadbox_height( & box) * overlap);
qt -> _root = quadnode_create ( & box);
assert (qt -> _root);
if ( ! qt -> _root)
{
free (qt);
qt = NULL;
}
}
assert (qt);
return qt;
}
/* destroys a quad tree */
void
quadtree_destroy (IN quadtree_t * qtree)
{
assert (qtree && qtree -> _root);
quadnode_destroy (qtree -> _root);
free (qtree);
}
/* inserts a node into quadtree and return pointer to new node */
quadnode_t *
quadtree_insert (IN quadtree_t * qtree,
IN long node_key,
IN quadbox_t * node_box
)
{
int depth = - 1 ;
return quadtree_insert_node (qtree, qtree -> _root, node_key, node_box, & depth);
}
/* searches nodes inside search_box */
void
quadtree_search (IN const quadtree_t * qtree,
IN quadbox_t * search_box,
OUT list_t * results_list
)
{
quadtree_search_nodes (qtree -> _root, search_box, results_list);
}
* quadtree.c
* Quad tree implementation -- for spatial quick searching
* cheungmine
* Oct. 5, 2007. All rights reserved.
*/
#include " quadtree.h "
#include < assert.h >
/* =============================================================================
Private Functions
============================================================================= */
static BOOL quadbox_is_valid (quadbox_t * qb)
{
return (qb -> _xmin < qb -> _xmax && qb -> _ymin < qb -> _ymax) ? TRUE : FALSE;
}
static double quadbox_width ( const quadbox_t * qb)
{
return (qb -> _xmax - qb -> _xmin);
}
static double quadbox_height ( const quadbox_t * qb)
{
return (qb -> _ymax - qb -> _ymin);
}
static void quadbox_init (quadbox_t * qb,
double xmin, double ymin, double xmax, double ymax)
{
qb -> _xmin = xmin; qb -> _ymin = ymin; qb -> _xmax = xmax; qb -> _ymax = ymax;
assert (quadbox_is_valid (qb) );
}
static void quadbox_inflate(quadbox_t * qb, double dx, double dy)
{
assert (dx > 0 && dy > 0 );
qb -> _xmin -= (dx / 2 );
qb -> _xmax += (dx / 2 );
qb -> _ymin -= (dy / 2 );
qb -> _ymax += (dy / 2 );
}
/* splits the quadrant such as below:
nw(0010) | ne(0001)
----------|----------
sw(0100) | se(1000)
*/
static void quadbox_split( const quadbox_t * qb,
quadbox_t * ne, quadbox_t * nw, quadbox_t * se, quadbox_t * sw,
float overlap)
{
double dx = quadbox_width(qb) * ( 1.0 + overlap) / 2 ;
double dy = quadbox_height(qb) * ( 1.0 + overlap) / 2 ;
assert (overlap >= QBOX_OVERLAP_MIN - 0.0001 );
assert (overlap <= QBOX_OVERLAP_MAX + 0.0001 );
quadbox_init (ne, qb -> _xmax - dx, qb -> _ymax - dy, qb -> _xmax, qb -> _ymax);
quadbox_init (nw, qb -> _xmin, qb -> _ymax - dy, qb -> _xmin + dx, qb -> _ymax);
quadbox_init (sw, qb -> _xmin, qb -> _ymin, qb -> _xmin + dx, qb -> _ymin + dy);
quadbox_init (se, qb -> _xmax - dx, qb -> _ymin, qb -> _xmax, qb -> _ymin + dy);
}
/* returns TRUE if the first is inside the senond */
static BOOL quadbox_is_inside( const quadbox_t * _first, const quadbox_t * _second)
{
return (_second -> _xmin < _first -> _xmin && _second -> _xmax > _first -> _xmax &&
_second -> _ymin < _first -> _ymin && _second -> _ymax > _first -> _ymax) ? TRUE : FALSE;
}
/* returns TRUE if two quad_box is overlapped */
static BOOL quadbox_is_overlapped( const quadbox_t * _first, const quadbox_t * _second)
{
return (_first -> _xmin > _second -> _xmax || _first -> _xmax < _second -> _xmin ||
_first -> _ymin > _second -> _ymax || _first -> _ymax < _second -> _ymin) ? FALSE : TRUE;
}
static quadnode_t * quadnode_create ( const quadbox_t * box)
{
quadnode_t * node = (quadnode_t * ) calloc ( 1 , sizeof (quadnode_t));
if (node)
memcpy ( & (node -> _box), box, sizeof (quadbox_t));
return node;
}
static void quadnode_destroy(quadnode_t * node)
{
if (node -> _sub[NE] != 0 ){
quadnode_destroy (node -> _sub[NE]);
quadnode_destroy (node -> _sub[NW]);
quadnode_destroy (node -> _sub[SE]);
quadnode_destroy (node -> _sub[SW]);
}
if (node -> _lst)
list_destroy(node -> _lst, NULL);
free (node);
}
static void quadnode_create_child(quadnode_t * node, float overlap, int depth)
{
quadbox_t ne, nw, se, sw;
assert (node);
quadbox_split ( & (node -> _box), & ne, & nw, & se, & sw, overlap );
node -> _sub[NE] = quadnode_create ( & ne);
node -> _sub[NW] = quadnode_create ( & nw);
node -> _sub[SW] = quadnode_create ( & sw);
node -> _sub[SE] = quadnode_create ( & se);
}
static BOOL quadnode_has_child( const quadnode_t * node)
{
return (node -> _sub[NE] != 0 );
}
static BOOL quadnode_has_data ( const quadnode_t * node)
{
return (node -> _lst && node -> _lst -> size > 0 ) ? TRUE: FALSE;
}
static void quadnode_add_data ( quadnode_t * node, long node_key )
{
assert (node);
if ( ! node -> _lst) node -> _lst = list_create();
assert (node -> _lst);
list_append_node (node -> _lst, list_key_create (node_key));
}
/* inserts a node to parent node of tree. returns pointer to node */
static quadnode_t * quadtree_insert_node ( quadtree_t * tree,
quadnode_t * parent,
long node_key,
quadbox_t * node_box,
int * depth
)
{
if ( quadbox_is_inside (node_box, & (parent -> _box)) )
{
if ( ++ ( * depth) < tree -> _depth )
{
if ( ! quadnode_has_child ( parent ) )
quadnode_create_child (parent, tree -> _overlap, ( * depth));
if ( quadbox_is_inside (node_box, & (parent -> _sub[NE] -> _box) ) )
return quadtree_insert_node (tree, parent -> _sub[NE], node_key, node_box, depth);
if ( quadbox_is_inside (node_box, & (parent -> _sub[NW] -> _box) ) )
return quadtree_insert_node (tree, parent -> _sub[NW], node_key, node_box, depth);
if ( quadbox_is_inside (node_box, & (parent -> _sub[SW] -> _box) ) )
return quadtree_insert_node (tree, parent -> _sub[SW], node_key, node_box, depth);
if ( quadbox_is_inside (node_box, & (parent -> _sub[SE] -> _box) ) )
return quadtree_insert_node (tree, parent -> _sub[SE], node_key, node_box, depth);
}
/* inserts into this node since it can NOT be included in any subnodes */
quadnode_add_data (parent, node_key);
return parent;
}
return NULL;
}
/* searched tree nodes */
static void quadtree_search_nodes (quadnode_t * current_node,
quadbox_t * search_box,
list_t * results_list
)
{
if ( quadbox_is_overlapped ( & (current_node -> _box), search_box ) )
{
if ( quadnode_has_data (current_node) )
list_append_node (results_list, list_node_create(current_node));
if ( quadnode_has_child (current_node) )
{
quadtree_search_nodes (current_node -> _sub[NE], search_box, results_list);
quadtree_search_nodes (current_node -> _sub[NW], search_box, results_list);
quadtree_search_nodes (current_node -> _sub[SW], search_box, results_list);
quadtree_search_nodes (current_node -> _sub[SE], search_box, results_list);
}
}
}
/* =============================================================================
Public Functions
============================================================================= */
quadtree_t *
quadtree_create (quadbox_t box,
int depth, /* 4~8 */
float overlap /* 0.02 ~ 0.4 */
)
{
quadtree_t * qt = NULL;
assert (depth >= QTREE_DEPTH_MIN - 0.0001 && depth <= QTREE_DEPTH_MAX + 0.0001 );
assert (overlap >= QBOX_OVERLAP_MIN - 0.0001 && overlap <= QBOX_OVERLAP_MAX + 0.0001 );
qt = (quadtree_t * ) calloc ( 1 , sizeof (quadtree_t));
if (qt)
{
qt -> _depth = depth;
qt -> _overlap = overlap;
quadbox_inflate ( & box, quadbox_width( & box) * overlap, quadbox_height( & box) * overlap);
qt -> _root = quadnode_create ( & box);
assert (qt -> _root);
if ( ! qt -> _root)
{
free (qt);
qt = NULL;
}
}
assert (qt);
return qt;
}
/* destroys a quad tree */
void
quadtree_destroy (IN quadtree_t * qtree)
{
assert (qtree && qtree -> _root);
quadnode_destroy (qtree -> _root);
free (qtree);
}
/* inserts a node into quadtree and return pointer to new node */
quadnode_t *
quadtree_insert (IN quadtree_t * qtree,
IN long node_key,
IN quadbox_t * node_box
)
{
int depth = - 1 ;
return quadtree_insert_node (qtree, qtree -> _root, node_key, node_box, & depth);
}
/* searches nodes inside search_box */
void
quadtree_search (IN const quadtree_t * qtree,
IN quadbox_t * search_box,
OUT list_t * results_list
)
{
quadtree_search_nodes (qtree -> _root, search_box, results_list);
}
其中用到的文件(list.h,list.c,unistd.h)参考我的另一篇文章:
C语言实现一个简单的单向链表list