我出于各种原因编写B +树,我来这里询问有关其节点实现的问题。我的节点目前看起来像:
struct BPlusNode
{
public:
//holds the list of keys
keyType **keys;
//stores the number of slots used
size_t size;
//holds the array of pointers to lower nodes NULL if this is a leaf node
BPlusNode **children;
//holds the pointer to the next load to the 'left'
BPlusNode *next;
//Data page pointers NULL if this is a branch node
Bucket **pages;
};
你可以看到我当前的实现是在我想知道是否应该使用* *或*的地方使用* *。
我很清楚* *需要两个解除引用操作,因此比简单地使用*慢,但是这个类使用了大量的递归,并且将指针传递给递归的子调用更方便功能。要用*执行此操作,我需要执行指针运算并传递结果指针。
**
someFunction(BPlusNode* currNode)
{
......
someFunction(currNode->children[ChildIndex]);
}
与*
someFunction(BPlusNode* currNode)
{
......
someFunction((currNode->children) + ChildIndex);
}
我可以看到有一个额外的内存读取来生成* *版本中所需的指针,但* *版本也更容易为我考虑(它更符合我如何看待绘制的图表在“计算机程序设计的艺术”和维基百科上。)
有没有人有这样或那样的想法?建议第三种选择?证明为什么一个优于另一个?等?
编辑:
我可能会在下面发布这个答案,但我只是意识到使用* *方案我不需要复制每个子节点或存储桶的全部内容我应该在数组的中间插入一个(即调整数组的大小) 。如果在重新分配数组时有* 20方案的子节点,我需要复制20 * sizeof(BPlusNode)字节,而不是* *方案的20 * sizeof(BPlusNode *)字节。
另一方面,我发现,由于我执行所有插入操作并且页面拆分是预先完成的,因此执行它们的效率提高是不必要的,并且* over * *在搜索中的好处超过了它。 / p>
答案 0 :(得分:2)
我将为键和指针数据定义另一个结构。我会承诺使用固定大小的节点,这些节点应该与你的磁盘结构相匹配。这使得树的内存映射变得更加容易。
你的BPlusNode结构变成了一个句柄类,它指向这些映射的数据节点,并通过在兄弟树下到树时读取它们来合成诸如prev和next指针之类的东西。
它可能如下所示:
enum BPlusNodeType {
LEAF, BRANCH
};
struct BPlusNodeData {
static const size_t max_size = 511; // Try to fit into 4K? 8K?
uint16_t size;
uint16_t type;
keyType key[max_size];
union {
Bucket* data[max_size];
BPlusNodeData* children[max_size];
};
};
答案 1 :(得分:1)
使用**
,您需要一个额外的分配步骤来保存每个BPlusNode*
子指针。或者您可以分配它们的块,只需让children
中的每个指针指向此块内的顺序BPlusNode*
元素 - 但它仍然是每个节点创建一个额外的动态内存分配(以及相应的额外释放)踩破坏)。所以我绝对建议使用一个*
。如果写
someFunction((currNode->children) + ChildIndex);
伤害你,你可以把它重写为
someFunction(&currNode->children[ChildIndex]);
我觉得更清楚。
答案 2 :(得分:0)
您最好使用STL“vector<keyType *> keys
”和“vector<BPlusNode *> children
”,依此类推?
这可能过于简单了,但我的印象是C ++中并不经常需要双重间接(并不是C语言中经常使用双重间接,尽管比C ++更常见)。