基数树

原理参考《算法导论》

注:代码设定,支持索引的位数最大是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是树高

所有代码均经测试,结果正确。

原文地址:https://www.cnblogs.com/dalgleish/p/8983552.html