数据结构——B树(附C++实现代码)

定义

一个 m 阶的B树是一个有以下属性的树:

  1. 每一个节点最多有 m 个子节点
  2. 每一个非叶子节点(除根节点)最少有 ⌈m/2⌉ 个子节点
  3. 如果根节点不是叶子节点,那么它至少有两个子节点
  4. k 个子节点的非叶子节点拥有 k − 1 个键
  5. 所有的叶子节点都在同一层

每一个内部节点的键将节点的子树分开。例如,如果一个内部节点有3个子节点(子树),那么它就必须有两个键: a1 和 a2 。左边子树的所有值都必须小于 a1 ,中间子树的所有值都必须在 a1 和a2 之间,右边子树的所有值都必须大于 a2 。

注:由于m=2时B树可以退化成二叉搜索树(BST),且m=2时B树的删除与m>=3时的B树的删除不兼容,因此本文实现的代码仅支持m>=3的B树,同时也会介绍为什么m=2时B树的删除不兼容。

结构

B树
template>
class BTree{
private:
    struct Node{
        ......
    };
     Compare compare;
    int size;
    Node *root;
    
    std::stack findNodeByKey(Key key);
    void adjustNodeForInsert(Node *node,Node* parent);
    void adjustNodeForRemove(Node *node,Node* parent);
    void maintainAfterInsert(std::stack& nodePathStack);
    void maintainAfterRemove(std::stack& nodePathStack);

public:
    BTree(){
        assert(order>=3);
        this->size = 0;
        this->root = nullptr;
        this->compare = Compare();
    }
    int insert(Key key,Value value);
    int remove(Key key);
    void levelOrderTraversal();
};
节点维护的信息
成员名 类型 描述
n int 节点数量
keys Key[] 节点键值的数组,具有唯一性和可排序性
values Value[] 节点保存的值的数组
children Node*[] 节点指向孩子们的数组
对keys[i]而言,children[i]是其左子树,children[i+1]是其右子树
节点结构定义
struct Node{
    // 对根节点 n满足 1 <= n <= order-1
    // 除根节点外 n满足 ((order-1)>>1) <= n <= order-1
    int n;

    // 大小设置为order,保证上溢出时也可插入
    Key keys[order];
    Value values[order];
    // 大小设置为order+1,保证上溢出时也可插入
    Node* children[order+1];

    // 构造函数
    Node():n(0){
        for(auto &p:children) p = nullptr;
    }

    // 二分查找,返回第一个大于等于该节点key的下标
    inline int search(Key key,const Compare& compare) const noexcept{
        // 避免因为极端情况导致的查询效果低下
        if(!this->n || !compare(this->keys[0],key)) return 0;
        if(this->n && compare(this->keys[this->n-1],key)) return this->n;
        int i=1,j=this->n-1;
        while(i<=j){
            int mid = i+((j-i)>>1);
            if(this->keys[mid] == key) return mid;
            if(compare(this->keys[mid],key)) i = mid + 1;
            else j = mid - 1;
        }
        return i;
    }
    
	// 判断节点是否为叶子节点
    inline bool isLeaf() const noexcept{
        return this->children[0] == nullptr;
    }
    
    // 判断节点是否含有key
    inline bool hasKey(Key key,const Compare& compare) const noexcept{
        int arg = this->search(key,compare);
        return this->keys[arg] == key;
    }
	
    // 更新节点
    inline void update(Key key,Value value,const Compare& compare){
        int arg = this->search(key,compare);
        this->values[arg] = value;
    }

    // 插入节点及其右子树
    inline void insert(Key key,Value value,Node* rightChild,const Compare& compare){
        int arg = this->search(key,compare);
        for(int i=this->n;i>arg;i--){
            this->keys[i] = this->keys[i-1];
            this->values[i] = this->values[i-1];
            this->children[i+1] = this->children[i];
        }
        this->keys[arg] = key;
        this->values[arg] = value;
        this->children[arg+1] = rightChild;
        this->n++; 
    }

    // 删除节点及其右子树
    inline void remove(Key key,const Compare& compare){
        int arg = this->search(key,compare);
        for(int i=arg;in-1;i++){
            this->keys[i] = this->keys[i+1];
            this->values[i] = this->values[i+1];
            this->children[i+1] = this->children[i+2];
        }
        this->children[this->n] = nullptr;
        this->n--;
    }

    // 上溢出(n >= order)的时候调用,分裂成左右子树,自身变成左子树,返回右子树
    inline Node* split(){
        Node* newNode = new Node();
        int mid = ((order-1)>>1);
        newNode->children[0] = this->children[mid+1];
        this->children[mid+1] = nullptr;
        for(int i=0,j=mid+1;jn;i++,j++){
            newNode->keys[i] = this->keys[j];
            newNode->values[i] = this->values[j];
            newNode->children[i+1] = this->children[j+1];
            newNode->n++;
            this->children[j+1] = nullptr;
        }
        this->n = mid;
        return newNode;
    }

    // 下溢出(n < ((order-1)>>1))且兄弟无法借出节点时调用,和右兄弟合并
    inline void merge(Key key,Value value,Node *rightSibling){
        this->keys[this->n] = key;
        this->values[this->n] = value;
        this->children[this->n+1] = rightSibling->children[0];
        this->n++;
        for(int i=0;in;i++){
            this->keys[this->n] = rightSibling->keys[i];
            this->values[this->n] = rightSibling->values[i];
            this->children[this->n+1] = rightSibling->children[i+1];
            this->n++;
        }
        delete rightSibling;
    }
};

前置说明

查找含有key的节点

具体流程

B树本身也是搜索树,可以直接按照搜索树的搜索流程进行,由于查找路径会在插入和删除时用到,因此可以直接返回保存了查找路径的栈,则栈顶即为含有key的节点

注:调用此函数需要保证根节点存在

实现代码
/**
 * @brief  查找含有key的节点(需保证根节点存在)
 * @param  key 键值
 * @return 保存了查找路径的栈,栈顶即为含有key的节点
 */
template
std::stack::Node*> BTree::findNodeByKey(Key key){
    std::stack nodePathStack;
    Node* node = this->root;
	nodePathStack.push(node);
    while(node->children[0]){
        int arg = node->search(key,this->compare);
        if(argn && node->keys[arg]==key) break;
        node = node->children[arg];
		nodePathStack.push(node);
    }
    return nodePathStack;
}

B树的插入

具体流程
  1. 首先检查根节点是否存在,若不存在则创建根节点并插入新的数据,插入成功并返回
  2. 查找要插入的键是否已存在,若存在,则直接更新数据并返回
  3. 向叶子节点直接插入数据
  4. 插入新的数据后可能会导致节点上溢出,因此需要对插入新数据后所有受到影响的节点(也就是查询路径上的节点)进行维护
实现代码
/**
 * @brief  插入键值对
 * @param  key 新的键
 * @param  value 新的值
 * @return int  0表示插入成功,1表示节点已存在,更新value
 */
template
int BTree::insert(Key key,Value value){
    if(this->root==nullptr){
       this->root = new Node();
       this->root->insert(key,value,nullptr,this->compare);
       return 0;
    }
    std::stack nodePathStack = this->findNodeByKey(key);
    Node *node = nodePathStack.top();
    if(node->hasKey(key,this->compare)){
        node->update(key,value,this->compare);
        return 1;
    }
    node->insert(key,value,nullptr,this->compare);
    this->maintainAfterInsert(nodePathStack);
    this->size++;
    return 0;
}

B树插入后的维护

插入后的维护

具体流程

B树插入后的维护即是检查被维护的节点是否上溢出(保存数据的数量n==order),若未溢出,则无需维护当前和后续节点,反之若上溢出,则当前节点需要结合其父节点进行调整。注意若出现根节点上溢出,则需要创建一个新节点作为根节点,并让当前节点作为其第一个孩子进行调整父子间的调整

实现代码
/**
 * @brief  插入新数据后的维护
 * @param  nodePathStack 保存了因为插入而受到影响的节点的栈
 * @return void
 */
template
void BTree::maintainAfterInsert(std::stack& nodePathStack){
    Node *node,*parent;
    node = nodePathStack.top();nodePathStack.pop();
    while(!nodePathStack.empty()){
        parent = nodePathStack.top();nodePathStack.pop();
        if(node->n < order) return ;
        this->adjustNodeForInsert(node,parent);
        node = parent;
    }
    if(node->n < order) return ;
    this->root = new Node();
    parent = this->root;
    parent->children[0] = node;
    this->adjustNodeForInsert(node, parent);
}

父子间的调整

具体流程

将上溢出的子节点根据中间数分成不包括中间数的左右两个部分,该过程可以调用Node结构体函数split函数实现,其可以将自身分成左右两个节点,自身变为左节点,并返回右节点,最后将中间数和右节点插入父节点即可

实现代码
/**
 * @brief  调整上溢出节点
 * @param  node 上溢出节点
 * @param  parent 上溢出节点父亲
 * @return void
 */
template
void BTree::adjustNodeForInsert(Node *node,Node* parent){
    // parent:        ...  ...                   ... mid ...
    //                   /           =====>         /   | 
    // node:      [left] mid [right]           [left] [right]
    int mid = ((order-1)>>1);
    Key key = node->keys[mid];
    Value value = node->values[mid];
    Node *rightChild = node->split();
    parent->insert(key,value,rightChild,this->compare);
}

B树的删除

具体流程
  1. 首先检查根节点是否存在,若不存在,则删除失败
  2. 检查被删除的键是否存在,若不存在,则删除失败
  3. 检查节点是否为叶子节点,若不是,则找到其直接后继进行替换,将要删除的数据变成直接后继,并相应的更新查询路径
  4. 从节点中删除键为key的数据
  5. 删除数据后可能会导致节点下溢出,因此需要对删除数据后所有受到影响的节点(也就是查询路径上的节点)进行维护
实现代码
/**
 * @brief  根据键删除数据
 * @param  key 要被删除的数据的键
 * @return int  0表示删除成功,1表示键不存在,删除失败
 */
template
int BTree::remove(Key key){
    if(this->root == nullptr) return 1;
    std::stack nodePathStack = this->findNodeByKey(key);
    Node *node = nodePathStack.top();
    if(!node->hasKey(key,this->compare)) return 1;
    // 检查节点是否为叶子节点,若不是,则找到其直接后继进行替换,将要删除的数据变成直接后继,并相应的更新查询路径
    if(!node->isLeaf()){
		Node* temp = node;
        int arg = node->search(key,this->compare);
        node = node->children[arg+1];
        nodePathStack.push(node);
        while(!node->isLeaf()){
            node = node->children[0];
            nodePathStack.push(node);
        }
		temp->keys[arg] = node->keys[0];
		temp->values[arg] = node->values[0];
		key = node->keys[0];
    }
    node->remove(key,this->compare);
    this->maintainAfterRemove(nodePathStack);
    this->size--;
    return 0;
}

B树删除后的维护

删除后的维护

具体流程

B树删除后的维护即是检查被维护的节点是否下溢出(保存数据的数量n<(order+1)/2-1),若未溢出,则无需维护当前和后续节点,反之若下溢出,则当前节点需要结合其父节点进行调整。注意若当前节点是根节点,且其因为删除或删除后维护而造成数据量归0,则需要更新根节点为其第一个孩子(具体为什么则这样可以对照插入的流程)

实现代码
/**
 * @brief  删除数据后的维护
 * @param  nodePathStack 保存了因为删除而受到影响的节点的栈
 * @return void
 */
template
void BTree::maintainAfterRemove(std::stack& nodePathStack){
    int mid = (order-1)>>1;
    Node *node,*parent;
    node = nodePathStack.top();nodePathStack.pop();
    while(!nodePathStack.empty()){
        parent = nodePathStack.top();nodePathStack.pop();
        if(node->n >= mid) return ;
        this->adjustNodeForRemove(node,parent);
        node = parent;   
    }
    if(this->root->n) return ;
    this->root = node->children[0];
    delete node;
}

父子间的调整

具体流程

case 1:左兄弟或右兄弟可以借出一个数据

​ 左右兄弟借出的数据不能直接给到子节点,不然会破坏搜索树的性质,此时可以将父节点中,左右子树分别为子节点和兄弟的数据下放到子节点,同时将兄弟借出的数据替换父节点下放的数据

  • 若是左兄弟借出一个数据,则该数据为左兄弟的最后一个数据
  • 若是右兄弟借出一个数据,则该数据为右兄弟的第一个数据

case 2:左兄弟或右兄弟都不能借出一个数据

​ 此时子节点可以选择与其一个存在的兄弟进行合并,根据其左右顺序,可以分为合并的左节点和右节点。合并的流程是,将父节点中,左右子树分别为子节点和兄弟的数据下放到合并的左节点,之后与合并的右节点进行合并,合并的过程可以调用Node结构体函数merge函数,该函数可以接受父亲的数据和合并的右节点进行合并,自身变成合并后的结果

实现代码
/**
 * @brief  调整下溢出节点
 * @param  node 下溢出节点
 * @param  parent 下溢出节点的父亲
 * @return void
 */
template
void BTree::adjustNodeForRemove(Node *node,Node* parent){
    int mid = ((order-1)>>1);
	int arg=-1;
	if(node->n) arg = parent->search(node->keys[0],this->compare);
	else while(parent->children[++arg]!=node);	
    Node* left = arg > 0 ? parent->children[arg-1] : nullptr;
    Node* right = arg < parent->n ? parent->children[arg+1] : nullptr;
    // case 1: 左兄弟或右兄弟可以借出一个数据
    if((left && left->n > mid) || (right && right->n > mid)){
        // 左兄弟借出一个数据
        //  parent:       ... key ...                        ... last ...
        //                  /     \         =====>             /      \ 
        //         [......]:last  [node]                  [......]   key:[node]
        if(left && left->n > mid){
            Key key = parent->keys[arg-1];
            Value value = parent->values[arg-1];
            node->insert(key,value,node->children[0],this->compare);
            node->children[0] = left->children[left->n];
            parent->keys[arg-1] = left->keys[left->n-1];
            parent->values[arg-1] = left->values[left->n-1];
            left->remove(left->keys[left->n-1],this->compare);
        }
        // 右兄弟借出一个数据
        //  parent:     ... key ...                        ... first ...
        //                /     \            =====>            /       \ 
        //           [node] first:[......]               [node]:key  [......]
        else if(right && right->n > mid){
            Key key = parent->keys[arg];
            Value value = parent->values[arg];
            node->insert(key,value,right->children[0],this->compare);
            right->children[0] = right->children[1];
            parent->keys[arg] = right->keys[0];
            parent->values[arg] = right->values[0];
            right->remove(right->keys[0],this->compare);
        }
        return ;
    }
    // case 2: 左兄弟或右兄弟都不能借出一个数据
    if(left){
        // 和左兄弟合并
        //  parent:       ... key ...                        ...  ...
        //                  /     \         =====>              /     
        //              [left]  [node]                  [left]:key:[node]   
        Key key = parent->keys[arg-1];
        Value value = parent->values[arg-1];
        left->merge(key,value,node);
        parent->remove(key,this->compare);
    }
    else if(right){
        // 和右兄弟合并
        //  parent:       ... key ...                        ...  ...
        //                  /     \         =====>              /     
        //              [node]  [right]                  [node]:key:[right]   
        Key key = parent->keys[arg];
        Value value = parent->values[arg];
        node->merge(key,value,right);
        parent->remove(key,this->compare);
    }
}

为什么m=2时会不兼容

m=2时,根据定义,此时每个节点至少需要有0个节点,也就是说没有数据的空节点是允许存在的,而对于非叶子节点的内的数据删除,其需要找到后继节点中的第1个数据进行替换删除,而由于空节点是允许存在的,此时找到的后继节点可能没有数据,也就无法进行数据的替换。

个人认为解决该问题的一个思路是引入“第0个数据”的概念,但碍于2阶B树的实际意义并不大,因此并没有继续深入研究

参考资料

OIwiki B树 https://oi-wiki.org/ds/b-tree/

B站 B树(B-树) - 来由, 定义, 插入, 构建 https://www.bilibili.com/video/BV1tJ4m1w7yR

B站 B树(B-树) - 删除 https://www.bilibili.com/video/BV1JU411d7iY

完整代码

#include 
#include 
#include 
#include 
#include 
#include   

template>
class BTree{
private:
    struct Node{
        // 对根节点 n满足 1 <= n <= order-1
        // 除根节点外 n满足 ((order-1)>>1) <= n <= order-1
        int n;

        // 大小设置为order,保证上溢出时也可插入
        Key keys[order];
        Value values[order];
        // 大小设置为order+1,保证上溢出时也可插入
        Node* children[order+1];

        // 构造函数
        Node():n(0){
            for(auto &p:children) p = nullptr;
        }

        // 二分查找,返回第一个大于等于该节点key的下标
        inline int search(Key key,const Compare& compare) const noexcept{
            // 避免因为极端情况导致的查询效果低下
            if(!this->n || !compare(this->keys[0],key)) return 0;
            if(this->n && compare(this->keys[this->n-1],key)) return this->n;
            int i=1,j=this->n-1;
            while(i<=j){
                int mid = i+((j-i)>>1);
                if(this->keys[mid] == key) return mid;
                if(compare(this->keys[mid],key)) i = mid + 1;
                else j = mid - 1;
            }
            return i;
        }

        // 判断节点是否为叶子节点
        inline bool isLeaf() const noexcept{
            return this->children[0] == nullptr;
        }

        // 判断节点是否含有key
        inline bool hasKey(Key key,const Compare& compare) const noexcept{
            int arg = this->search(key,compare);
            return this->keys[arg] == key;
        }

        // 更新节点
        inline void update(Key key,Value value,const Compare& compare){
            int arg = this->search(key,compare);
            this->values[arg] = value;
        }

        // 插入节点及其右子树
        inline void insert(Key key,Value value,Node* rightChild,const Compare& compare){
            int arg = this->search(key,compare);
            for(int i=this->n;i>arg;i--){
                this->keys[i] = this->keys[i-1];
                this->values[i] = this->values[i-1];
                this->children[i+1] = this->children[i];
            }
            this->keys[arg] = key;
            this->values[arg] = value;
            this->children[arg+1] = rightChild;
            this->n++; 
        }

        // 删除节点及其右子树
        inline void remove(Key key,const Compare& compare){
            int arg = this->search(key,compare);
            for(int i=arg;in-1;i++){
                this->keys[i] = this->keys[i+1];
                this->values[i] = this->values[i+1];
                this->children[i+1] = this->children[i+2];
            }
            this->children[this->n] = nullptr;
            this->n--;
        }

        // 上溢出(n >= order)的时候调用,分裂成左右子树,自身变成左子树,返回右子树
        inline Node* split(){
            Node* newNode = new Node();
            int mid = ((order-1)>>1);
            newNode->children[0] = this->children[mid+1];
            this->children[mid+1] = nullptr;
            for(int i=0,j=mid+1;jn;i++,j++){
                newNode->keys[i] = this->keys[j];
                newNode->values[i] = this->values[j];
                newNode->children[i+1] = this->children[j+1];
                newNode->n++;
                this->children[j+1] = nullptr;
            }
			this->n = mid;
            return newNode;
        }
		
		// 下溢出(n < ((order-1)>>1))且兄弟无法借出节点时调用,和右兄弟合并
        inline void merge(Key key,Value value,Node *rightSibling){
            this->keys[this->n] = key;
            this->values[this->n] = value;
            this->children[this->n+1] = rightSibling->children[0];
            this->n++;
            for(int i=0;in;i++){
                this->keys[this->n] = rightSibling->keys[i];
                this->values[this->n] = rightSibling->values[i];
                this->children[this->n+1] = rightSibling->children[i+1];
                this->n++;
            }
            delete rightSibling;
        }
    };
    Compare compare;
    int size;
    Node *root;
    
    std::stack findNodeByKey(Key key);
    void adjustNodeForInsert(Node *node,Node* parent);
    void adjustNodeForRemove(Node *node,Node* parent);
    void maintainAfterInsert(std::stack& nodePathStack);
    void maintainAfterRemove(std::stack& nodePathStack);

public:
    BTree(){
        assert(order>=3);
        this->size = 0;
        this->root = nullptr;
        this->compare = Compare();
    }
    int insert(Key key,Value value);
    int remove(Key key);
    void levelOrderTraversal();
};


/**
 * @brief  查找含有key的节点(需保证根节点存在)
 * @param  key 键值
 * @return 保存了查找路径的栈,栈顶即为含有key的节点
 */
template
std::stack::Node*> BTree::findNodeByKey(Key key){
    std::stack nodePathStack;
    Node* node = this->root;
	nodePathStack.push(node);
    while(node->children[0]){
        int arg = node->search(key,this->compare);
        if(argn && node->keys[arg]==key) break;
        node = node->children[arg];
		nodePathStack.push(node);
    }
    return nodePathStack;
}

/**
 * @brief  插入键值对
 * @param  key 新的键
 * @param  value 新的值
 * @return int  0表示插入成功,1表示节点已存在,更新value
 */
template
int BTree::insert(Key key,Value value){
    if(this->root==nullptr){
       this->root = new Node();
       this->root->insert(key,value,nullptr,this->compare);
       return 0;
    }
    std::stack nodePathStack = this->findNodeByKey(key);
    Node *node = nodePathStack.top();
    if(node->hasKey(key,this->compare)){
        node->update(key,value,this->compare);
        return 1;
    }
    node->insert(key,value,nullptr,this->compare);
    this->maintainAfterInsert(nodePathStack);
    this->size++;
    return 0;
}

/**
 * @brief  插入新数据后的维护
 * @param  nodePathStack 保存了因为插入而受到影响的节点的栈
 * @return void
 */
template
void BTree::maintainAfterInsert(std::stack& nodePathStack){
    Node *node,*parent;
    node = nodePathStack.top();nodePathStack.pop();
    while(!nodePathStack.empty()){
        parent = nodePathStack.top();nodePathStack.pop();
        if(node->n < order) return ;
        this->adjustNodeForInsert(node,parent);
        node = parent;
    }
    if(node->n < order) return ;
    this->root = new Node();
    parent = this->root;
    parent->children[0] = node;
    this->adjustNodeForInsert(node, parent);
}

/**
 * @brief  调整上溢出节点
 * @param  node 上溢出节点
 * @param  parent 上溢出节点的父亲
 * @return void
 */
template
void BTree::adjustNodeForInsert(Node *node,Node* parent){
    // parent:        ...  ...                   ... mid ...
    //                   /           =====>         /   | 
    // node:      [left] mid [right]           [left] [right]
    int mid = ((order-1)>>1);
    Key key = node->keys[mid];
    Value value = node->values[mid];
    Node *rightChild = node->split();
    parent->insert(key,value,rightChild,this->compare);
}

/**
 * @brief  根据键删除数据
 * @param  key 要被删除的数据的键
 * @return int  0表示删除成功,1表示键不存在,删除失败
 */
template
int BTree::remove(Key key){
    if(this->root == nullptr) return 1;
    std::stack nodePathStack = this->findNodeByKey(key);
    Node *node = nodePathStack.top();
    if(!node->hasKey(key,this->compare)) return 1;
    // 检查节点是否为叶子节点,若不是,则找到其直接后继进行替换,将要删除的数据变成直接后继,并相应的更新查询路径
    if(!node->isLeaf()){
		Node* temp = node;
        int arg = node->search(key,this->compare);
        node = node->children[arg+1];
        nodePathStack.push(node);
        while(!node->isLeaf()){
            node = node->children[0];
            nodePathStack.push(node);
        }
		temp->keys[arg] = node->keys[0];
		temp->values[arg] = node->values[0];
		key = node->keys[0];
    }
    node->remove(key,this->compare);
    this->maintainAfterRemove(nodePathStack);
    this->size--;
    return 0;
}

/**
 * @brief  删除数据后的维护
 * @param  nodePathStack 保存了因为删除而受到影响的节点的栈
 * @return void
 */
template
void BTree::maintainAfterRemove(std::stack& nodePathStack){
    int mid = (order-1)>>1;
    Node *node,*parent;
    node = nodePathStack.top();nodePathStack.pop();
    while(!nodePathStack.empty()){
        parent = nodePathStack.top();nodePathStack.pop();
        if(node->n >= mid) return ;
        this->adjustNodeForRemove(node,parent);
        node = parent;   
    }
    if(this->root->n) return ;
    this->root = node->children[0];
    delete node;
}

/**
 * @brief  调整下溢出节点
 * @param  node 下溢出节点
 * @param  parent 下溢出节点的父亲
 * @return void
 */
template
void BTree::adjustNodeForRemove(Node *node,Node* parent){
    int mid = ((order-1)>>1);
	int arg=-1;
	if(node->n) arg = parent->search(node->keys[0],this->compare);
	else while(parent->children[++arg]!=node);	
    Node* left = arg > 0 ? parent->children[arg-1] : nullptr;
    Node* right = arg < parent->n ? parent->children[arg+1] : nullptr;
    // case 1: 左兄弟或右兄弟可以借出一个数据
    if((left && left->n > mid) || (right && right->n > mid)){
        // 左兄弟借出一个数据
        //  parent:       ... key ...                        ... last ...
        //                  /     \         =====>             /      \ 
        //         [......]:last  [node]                  [......]   key:[node]
        if(left && left->n > mid){
            Key key = parent->keys[arg-1];
            Value value = parent->values[arg-1];
            node->insert(key,value,node->children[0],this->compare);
            node->children[0] = left->children[left->n];
            parent->keys[arg-1] = left->keys[left->n-1];
            parent->values[arg-1] = left->values[left->n-1];
            left->remove(left->keys[left->n-1],this->compare);
        }
        // 右兄弟借出一个数据
        //  parent:     ... key ...                        ... first ...
        //                /     \            =====>            /       \ 
        //           [node] first:[......]               [node]:key  [......]
        else if(right && right->n > mid){
            Key key = parent->keys[arg];
            Value value = parent->values[arg];
            node->insert(key,value,right->children[0],this->compare);
            right->children[0] = right->children[1];
            parent->keys[arg] = right->keys[0];
            parent->values[arg] = right->values[0];
            right->remove(right->keys[0],this->compare);
        }
        return ;
    }
    // case 2: 左兄弟或右兄弟都不能借出一个数据
    if(left){
        // 和左兄弟合并
        //  parent:       ... key ...                        ...  ...
        //                  /     \         =====>              /     
        //              [left]  [node]                  [left]:key:[node]   
        Key key = parent->keys[arg-1];
        Value value = parent->values[arg-1];
        left->merge(key,value,node);
        parent->remove(key,this->compare);
    }
    else if(right){
        // 和右兄弟合并
        //  parent:       ... key ...                        ...  ...
        //                  /     \         =====>              /     
        //              [node]  [right]                  [node]:key:[right]   
        Key key = parent->keys[arg];
        Value value = parent->values[arg];
        node->merge(key,value,right);
        parent->remove(key,this->compare);
    }
}

/**
 * @brief  B树的中序遍历
 * @return void
 */
template
void BTree::levelOrderTraversal(){
    std::queue q;
    q.push(this->root);
    while(!q.empty()){
        int layerSize = q.size();
        while(layerSize--){
            Node *node = q.front();q.pop(); 
            if(!node) continue;
            int i;          
            for(i=0;in;i++){
                std::cout << node->keys[i] << " ";
                q.push(node->children[i]);
            }   
			std::cout << " | ";
            q.push(node->children[i]);
        }
        std::cout << std::endl;
    }
}

你可能感兴趣的:(数据结构,b树)