原理参考《算法导论》
注:代码设定,支持索引的位数最大是32位,当然可以修改让其支持64和32位的索引
首先定义一些常量(修改此处的常量,让其支持64位的索引)
#define MAX_BITS 0x80000000 #define INT_BITS 32 //32位下的int
然后定义一个基数树类
template <typename T, int bits, int height>//bits代表索引的位数,height代表高度, 暂时支持32位 class radix_tree { public: struct _Nodetype; typedef union _Autotype { _Nodetype *DeepNodes;//下一层 T *slots; }Autotype; typedef struct _Nodetype { _Nodetype() { autov.DeepNodes = NULL; } _Nodetype(_Nodetype *node) { autov.DeepNodes = node; } _Nodetype(T *s) { autov.slots = s; } Autotype autov; }Nodetype, *pNodetype; radix_tree() { root = new Nodetype((pNodetype)NULL); } ~radix_tree() { } void radix_insert(int index, T key);//T类型应用时是指针,指向对象 T radix_search(int index); void radix_delete(int index);//惰性删除 void radix_empty();//未实现,建议采用双线程池,这样方便这个函数回收内存 private: int radix_mask(int h); int radix_index(const int index, int h); pNodetype root; };
关于类成员实现
radix_mask成员负责根据所在树的层数,动态生成掩码。比如本例中使用18位的索引值,然后定义树高为3,那么在第一层的掩码是0x3F000,第二层的掩码是0xFC0,第三层的掩码是0x3F
template <typename T, int bits, int height> int radix_tree<T, bits, height>::radix_mask(int h) {//1到height层 int slotbits = bits / height, skipbits = INT_BITS - bits + (h - 1)*slotbits, tMask = 0; if (h<1 || h>height) return -1; while (skipbits < INT_BITS - bits + slotbits*h) { tMask |= (MAX_BITS >> skipbits); skipbits++; } return tMask; }
radix_index成员负责提取索引中的偏移值。比如18位索引,第一层的时候,掩码为0x3F000,那么通过此掩码提取出来的6位偏移值,用于查找第一层的查找。其余层数以此类推。
template <typename T, int bits, int height> int radix_tree<T, bits, height>::radix_index(const int index, int h) { int slotbits = bits / height, lowerMask = radix_mask(height), mask; if (h<1 || h>height) return -1; mask = radix_mask(h);//更新mask return (index & mask) >> (bits / height*(height - h)) & lowerMask; }
radix_insert成员通过提供的索引值,插入到指定位置
template <typename T, int bits, int height> void radix_tree<T, bits, height>::radix_insert(int index, T key) { pNodetype node = root; for (int tree_h = 1; tree_h < height; tree_h++) {//目录,没有数据 if (node->autov.DeepNodes == NULL) node->autov.DeepNodes = new Nodetype[pow(2, bits / height)]; node = &node->autov.DeepNodes[radix_index(index, tree_h)]; } //录入数据 if (node->autov.slots==NULL) node->autov.slots = new T[pow(2, bits / height)]; node->autov.slots[radix_index(index, height)] = key; }
radix_search成员,原理和radix_insert成员一样,但是不负责分配,只负责查找
template <typename T, int bits, int height> T radix_tree<T, bits, height>::radix_search(int index) { pNodetype node = root; for (int tree_h = 1; tree_h < height; tree_h++) { if (node->autov.DeepNodes == NULL) return -1; node = &node->autov.DeepNodes[radix_index(index, tree_h)]; } //查询数据 if (node == root || node->autov.slots == NULL) return -1; return node->autov.slots[radix_index(index, height)]; }
radix_delete成员,采用惰性删除,仅将找到的节点数据变为-1,不删除节点,因为实际应用中,每个数据都是指针,指向对应对象,所以不存在负数
template <typename T, int bits, int height> void radix_tree<T, bits, height>::radix_delete(int index) { if (radix_search(index)) radix_insert(index, -1); }
radix_empty成员,删除所有节点以及数据
template <typename T, int bits, int height> void radix_tree<T, bits, height>::radix_empty() { //建议用线程池分配所有,这样方便释放
//这里未实现,思路是利用线程池,用来分配节点。这样释放时,整体释放线程池;否则比如18位的索引,必须得搜索0x0-0xffff范围的所有索引,然后逐一删除,这样很浪费时间。
}
分离定义模版类一定要提前实例化
template class radix_tree<int, 18, 3>;//index是18位的整数,3是树高
所有代码均经测试,结果正确。