参考资料:按第一个参考资料构建,代码基本上来自于第二个参考资料

https://www.cnblogs.com/guohai-stronger/p/9225057.html

https://www.cnblogs.com/newhand-liu/archive/2012/04/20/2793288.html

#include <iostream>
using namespace std;

static const int M=5; //阶
static const int KEY_MAX=M-1; //每个节点最多含有2个关键字
static const int KEY_MIN=2; //每个节点至少有1个关键字
static const int CHILD_MAX=KEY_MAX+1; //子
static const int CHILD_MIN=KEY_MIN+1;

template<class T>
struct Node
{
bool isLeaf;
int keyNum;
T keyValue[KEY_MAX+1];
Node<T>* pChild[CHILD_MAX+1];
};

template<class T>
class BTree{
public:
BTree(T a[],int n);
void Insert(T data);
bool Contain(T data);
void Display();
void Remove(T data);
void Clear();
private:
Node<T>* root;
void _clear(Node<T>** node);
T _getPredecessor(Node<T> *pNode);
T _getSuccessor(Node<T> *pNode);
void _mergeChild(Node<T> **pParent, int index);
void _recursive_remove(Node<T> **pNode, T key);
void _deleteNode(Node<T>** node);
void _insert(Node<T>** node,T data);
void _insertNonFull(Node<T>** node,T data);
void _splitChild(Node<T> **pParent, int nChildIndex, Node<T> **pChild);
bool _search(Node<T>* node,T data);
void _displayInConcavo(Node<T> *pNode, int count);
};

template<class T>
BTree<T>::BTree(T a[],int n)
{
root=NULL;
for(int i=0;i<n;i++)
_insert(&root,a[i]);
}

template<class T>
void BTree<T>::Insert(T data)
{
_insert(&root,data);
}

template<class T>
void BTree<T>::_insert(Node<T>** node,T data)
{
if(Contain(data))
return;
else
{
if(root==NULL)
{
root=new Node<T>;
root->isLeaf=true;
root->keyNum=0;
}
_insertNonFull(&root,data);
if(root->keyNum==KEY_MAX+1)
{
Node<T>* pNode=new Node<T>;
pNode->isLeaf=false;
pNode->keyNum=0;
pNode->pChild[0]=root;
_splitChild(&pNode, 0, &root);
root=pNode;
}
}
}

template<class T>
bool BTree<T>::Contain(T data)
{
_search(root,data);
}

template<class T>
bool BTree<T>::_search(Node<T>* node,T data)
{
if(node==NULL)
return false;
else
{
bool flag=false;
int i;
for(i=0;i<node->keyNum&&data>node->keyValue[i];i++);
if(i<node->keyNum&&data==node->keyValue[i])
return true;
else
{
if(node->isLeaf)
return false;
else
return _search(node->pChild[i],data);
}
}
}

//Parent新结点,pChild原根结点
template<class T>
void BTree<T>::_splitChild(Node<T> **pParent, int nChildIndex, Node<T> **pChild)
{
Node<T> *pRightNode=new Node<T>;
pRightNode->isLeaf=(*pChild)->isLeaf;
pRightNode->keyNum=(*pChild)->keyNum-KEY_MIN-1;

if(pRightNode->keyNum>0)
{
for(int i=0;i<pRightNode->keyNum;++i)
pRightNode->keyValue[i]=(*pChild)->keyValue[i+CHILD_MIN];
if(!(*pChild)->isLeaf)
{
for(int i=0;i<pRightNode->keyNum+1;++i)
pRightNode->pChild[i] = (*pChild)->pChild[i+CHILD_MIN];
}
}
(*pChild)->keyNum = KEY_MIN; //更新左子树的关键字个数

for (int i=(*pParent)->keyNum; i>nChildIndex; --i)//将父节点中的nChildIndex后的所有关键字的值和子树指针向后移一位
{
(*pParent)->pChild[i+1] = (*pParent)->pChild[i];
(*pParent)->keyValue[i] = (*pParent)->keyValue[i-1];
}
++(*pParent)->keyNum; //更新父节点的关键字个数
(*pParent)->pChild[nChildIndex+1] = pRightNode; //存储右子树指针
(*pParent)->keyValue[nChildIndex] = (*pChild)->keyValue[KEY_MIN];//把节点的中间值提到父节点
}

template<class T>
void BTree<T>::_insertNonFull(Node<T>** pNode,T key)
{
int i = (*pNode)->keyNum; //获取节点内关键字个数
if ((*pNode)->isLeaf) //pNode是叶子节点
{
while (i>0&&key<(*pNode)->keyValue[i-1]) //从后往前,查找关键字的插入位置
{
(*pNode)->keyValue[i] = (*pNode)->keyValue[i-1]; //向后移位
--i;
}
(*pNode)->keyValue[i] = key; //插入关键字的值
++(*pNode)->keyNum; //更新节点关键字的个数
}
else//pNode是内节点
{
while(i>0&&key<(*pNode)->keyValue[i-1]) //从后往前,查找关键字的插入的子树
--i;
Node<T> *pChild = (*pNode)->pChild[i]; //目标子树结点指针
_insertNonFull(&pChild, key); //插入关键字到目标子树节点
if (pChild->keyNum==KEY_MAX+1) //子树节点已满
{
_splitChild(pNode, i, &pChild);//分裂子树节点
if(key>(*pNode)->keyValue[i]) //确定目标子树
pChild = (*pNode)->pChild[i+1];
}

}
}

//用括号打印树
template<class T>
void BTree<T>::_displayInConcavo(Node<T> *pNode, int count)
{
if (pNode!=NULL)
{
int i, j;
for (i=0; i<pNode->keyNum; ++i)
{
if (!pNode->isLeaf)
{
_displayInConcavo(pNode->pChild[i], count-2);
}
for (j=count; j>=0; --j)
{
cout<<"-";
}
cout<<pNode->keyValue[i]<<endl;
}
if (!pNode->isLeaf)
{
_displayInConcavo(pNode->pChild[i], count-2);
}
}
}

template<class T>
void BTree<T>::Display()
{
_displayInConcavo(root,20); //count先随便写
}

template<class T>
void BTree<T>::Remove(T data)
{
if (!Contain(data)) //不存在
{
return;
}
if (root->keyNum==1)//特殊情况处理
{
if (root->isLeaf)
{
Clear();
return;
}
else
{
Node<T> *pChild1 = root->pChild[0];
Node<T> *pChild2 = root->pChild[1];
if (pChild1->keyNum==KEY_MIN&&pChild2->keyNum==KEY_MIN)
{
_mergeChild(&root, 0);
_deleteNode(&root);
root = pChild1;
}
}
}
_recursive_remove(&root, data);
}
template<class T>
void BTree<T>::Clear()
{
_clear(&root);
root = NULL;
}
template<class T>
void BTree<T>::_clear(Node<T>** pNode)
{
if (pNode!=NULL)
{
if (!(*pNode)->isLeaf)
{
for(int i=0; i<=(*pNode)->keyNum; ++i)
_clear(&(*pNode)->pChild[i]);
}
_deleteNode(pNode);
}
}
template<class T>
void BTree<T>::_deleteNode(Node<T>** pNode)
{
if (*pNode!=NULL)
{
delete *pNode;
*pNode = NULL;
}
}
template<class T>
void BTree<T>::_mergeChild(Node<T> **pParent, int index)
{
Node<T> *pChild1 = (*pParent)->pChild[index];
Node<T> *pChild2 = (*pParent)->pChild[index+1];
//将pChild2数据合并到pChild1
pChild1->keyNum = KEY_MAX+1;
pChild1->keyValue[KEY_MIN] = (*pParent)->keyValue[index];//将父节点index的值下移
int i;
for (i=0; i<KEY_MIN; ++i)
{
pChild1->keyValue[i+KEY_MIN+1] = pChild2->keyValue[i];
}
if (!pChild1->isLeaf)
{
for (i=0; i<CHILD_MIN; ++i)
{
pChild1->pChild[i+CHILD_MIN] = pChild2->pChild[i];
}
}

//父节点删除index的key,index后的往前移一位
--(*pParent)->keyNum;
for(i=index; i<(*pParent)->keyNum; ++i)
{
(*pParent)->keyValue[i] = (*pParent)->keyValue[i+1];
(*pParent)->pChild[i+1] = (*pParent)->pChild[i+2];
}
_deleteNode(&pChild2); //删除pChild2
}
template<class T>
void BTree<T>::_recursive_remove(Node<T> **pNode, T key)
{
int i=0;
while(i<(*pNode)->keyNum&&key>(*pNode)->keyValue[i])
++i;
if (i<(*pNode)->keyNum&&key==(*pNode)->keyValue[i])//关键字key在节点pNode中
{
if ((*pNode)->isLeaf)//pNode是个叶节点
{
//从pNode中删除k
--(*pNode)->keyNum;
for (; i<(*pNode)->keyNum; ++i)
{
(*pNode)->keyValue[i] = (*pNode)->keyValue[i+1];
}
return;
}
else//pNode是个内节点
{
Node<T> *pChildPrev = (*pNode)->pChild[i];//节点pNode中前于key的子节点
Node<T> *pChildNext = (*pNode)->pChild[i+1];//节点pNode中后于key的子节点
if (pChildPrev->keyNum>=CHILD_MIN)//节点pChildPrev中至少包含CHILD_MIN个关键字
{
T prevKey = _getPredecessor(pChildPrev); //获取key的前驱关键字
_recursive_remove(&pChildPrev, prevKey);
(*pNode)->keyValue[i] = prevKey; //替换成key的前驱关键字
return;
}
else if (pChildNext->keyNum>=CHILD_MIN)//节点pChildNext中至少包含CHILD_MIN个关键字
{
T nextKey = _getSuccessor(pChildNext); //获取key的后继关键字
_recursive_remove(&pChildNext, nextKey);
(*pNode)->keyValue[i] = nextKey; //替换成key的后继关键字
return;
}
else//节点pChildPrev和pChildNext中都只包含CHILD_MIN-1个关键字
{
_mergeChild(pNode, i);
_recursive_remove(&pChildPrev, key);
}
}
}
else//关键字key不在节点pNode中
{
Node<T> *pChildNode = (*pNode)->pChild[i];//包含key的子树根节点
if (pChildNode->keyNum==KEY_MIN)//只有t-1个关键字
{
Node<T> *pLeft = i>0 ? (*pNode)->pChild[i-1] : NULL; //左兄弟节点
Node<T> *pRight = i<(*pNode)->keyNum ? (*pNode)->pChild[i+1] : NULL;//右兄弟节点
int j;
if (pLeft&&pLeft->keyNum>=CHILD_MIN)//左兄弟节点至少有CHILD_MIN个关键字
{
//父节点中i-1的关键字下移至pChildNode中
for (j=pChildNode->keyNum; j>0; --j)
{
pChildNode->keyValue[j] = pChildNode->keyValue[j-1];
}
pChildNode->keyValue[0] = (*pNode)->keyValue[i-1];

if (!pLeft->isLeaf)
{
for (j=pChildNode->keyNum+1; j>0; --j) //pLeft节点中合适的子女指针移植到pChildNode中
{
pChildNode->pChild[j] = pChildNode->pChild[j-1];
}
pChildNode->pChild[0] = pLeft->pChild[pLeft->keyNum];
}
++pChildNode->keyNum;
(*pNode)->keyValue[i] = pLeft->keyValue[pLeft->keyNum-1];//pLeft节点中的最大关键字上升到pNode中
--pLeft->keyNum;
}
else if (pRight&&pRight->keyNum>=CHILD_MIN)//右兄弟节点至少有CHILD_MIN个关键字
{
//父节点中i的关键字下移至pChildNode中
pChildNode->keyValue[pChildNode->keyNum] = (*pNode)->keyValue[i];
++pChildNode->keyNum;
(*pNode)->keyValue[i] = pRight->keyValue[0];//pRight节点中的最小关键字上升到pNode中
--pRight->keyNum;
for (j=0; j<pRight->keyNum; ++j)
{
pRight->keyValue[j] = pRight->keyValue[j+1];
}
if (!pRight->isLeaf)
{
pChildNode->pChild[pChildNode->keyNum] = pRight->pChild[0];//pRight节点中合适的子女指针移植到pChildNode中
for (j=0; j<=pRight->keyNum; ++j)
{
pRight->pChild[j] = pRight->pChild[j+1];
}
}
}
//左右兄弟节点都只包含CHILD_MIN-1个节点
else if (pLeft)//与左兄弟合并
{
_mergeChild(pNode, i-1);
pChildNode = pLeft;
}
else if (pRight)//与右兄弟合并
{
_mergeChild(pNode, i);

}
}
_recursive_remove(&pChildNode, key);
}
}
template<class T>
T BTree<T>::_getPredecessor(Node<T> *pNode)
{
while (!pNode->isLeaf)
{
pNode = pNode->pChild[pNode->keyNum];
}
return pNode->keyValue[pNode->keyNum-1];
}
template<class T>
T BTree<T>::_getSuccessor(Node<T> *pNode)
{
while (!pNode->isLeaf)
{
pNode = pNode->pChild[0];
}
return pNode->keyValue[0];
}

C++实现的B树的更多相关文章

  1. B树——算法导论(25)

    B树 1. 简介 在之前我们学习了红黑树,今天再学习一种树--B树.它与红黑树有许多类似的地方,比如都是平衡搜索树,但它们在功能和结构上却有较大的差别. 从功能上看,B树是为磁盘或其他存储设备设计的, ...

  2. ASP.NET Aries 入门开发教程8:树型列表及自定义右键菜单

    前言: 前面几篇重点都在讲普通列表的相关操作. 本篇主要讲树型列表的操作. 框架在设计时,已经把树型列表和普通列表全面统一了操作,用法几乎是一致的. 下面介绍一些差距化的内容: 1:树型列表绑定: v ...

  3. 再讲IQueryable<T>,揭开表达式树的神秘面纱

    接上篇<先说IEnumerable,我们每天用的foreach你真的懂它吗?> 最近园子里定制自己的orm那是一个风生水起,感觉不整个自己的orm都不好意思继续混博客园了(开个玩笑).那么 ...

  4. HDU1671——前缀树的一点感触

    题目http://acm.hdu.edu.cn/showproblem.php?pid=1671 题目本身不难,一棵前缀树OK,但是前两次提交都没有成功. 第一次Memory Limit Exceed ...

  5. 算法与数据结构(十一) 平衡二叉树(AVL树)

    今天的博客是在上一篇博客的基础上进行的延伸.上一篇博客我们主要聊了二叉排序树,详情请戳<二叉排序树的查找.插入与删除>.本篇博客我们就在二叉排序树的基础上来聊聊平衡二叉树,也叫AVL树,A ...

  6. [C#] C# 知识回顾 - 表达式树 Expression Trees

    C# 知识回顾 - 表达式树 Expression Trees 目录 简介 Lambda 表达式创建表达式树 API 创建表达式树 解析表达式树 表达式树的永久性 编译表达式树 执行表达式树 修改表达 ...

  7. bzoj3207--Hash+主席树

    题目大意: 给定一个n个数的序列和m个询问(n,m<=100000)和k,每个询问包含k+2个数字:l,r,b[1],b[2]...b[k],要求输出b[1]~b[k]在[l,r]中是否出现. ...

  8. bzoj1901--树状数组套主席树

    树状数组套主席树模板题... 题目大意: 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i+2]--a[ ...

  9. bzoj3932--可持久化线段树

    题目大意: 最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分.超级计算机中的 任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第 ...

  10. jquery-treegrid树状表格的使用(.Net平台)

    上一篇介绍了DataTable,这一篇在DT的基础之上再使用jquery的一款插件:treegrid,官网地址:http://maxazan.github.io/jquery-treegrid/ 一. ...

随机推荐

  1. 线性基思想+贪心——cf1249C

    /*1+3+9+...+3^n<3^(n+1),按这个思路贪心一下就好*/#include<bits/stdc++.h> using namespace std; #define l ...

  2. BZOJ 2597: [Wc2007]剪刀石头布(费用流)

    传送门 解题思路 考虑全集-不能构成三元环的个数.如果三个点不能构成三元环,一定有一个点的入度为\(2\),继续扩展,如果一个点的度数为\(3\),则会失去3个三元环.对于一个点来说,它所产生的不能构 ...

  3. jmeter基本问题

    jmetet加压的时候不用图像界面(GUI),直接在命令行加压(命令行生成一个report-命令行参数),不做断言,不加监听器--不然会很卡: 进入就meter命令行: 后置处理器可以从HTML页面拿 ...

  4. robotframework + selenium2library 一点测试的经验

    1 对于元素的外层包括frame/iframe标签的.一定要先select  frame name=xxx,然后再操作元素. Select frame name=新建个案 click element ...

  5. JAVA常用集合解析

    ArrayList : 底层基于数组实现,在使用add方法添加元素时,首先校验集合容量,将新添加的值放入集合尾部并进行长度加一,进行自动扩容,扩容的操作时将数据中的所有元素复制到新的集合中. 在指定位 ...

  6. 9、TestNG介绍与安装

    介绍 TestNG是一个受JUnit和NUnit启发的测试框架,但引入了一些新功能,使其更加强大和易于使用,例如: 注释. 在任意大的线程池中运行你的测试,这些线程池有各种可用的策略(所有方法在他们自 ...

  7. 虚拟机安装VMware Tools, 安装gcc编译器

    一.虚拟机安装VMware Tools 1.虚拟机=>安装VMware Tools 2.打开文件,将下载的压缩包VMwareTools-10.3.10-12406962.tar.gz移动到指定安 ...

  8. Django框架(八)—— 模板层:模板导入、模板继承、静态文件

    目录 模板导入.继承.静态文件 一.模板导入 1.语法 2.使用 二.模板的继承 1.使用模板的继承 2.强调(注意点) 三.静态文件 1.在配置文件settings中配置过静态文件 2.导入使用静态 ...

  9. 令人清爽的异步函数async、await

    1.什么是async.await? async用于声明一个函数是异步的.而await从字面意思上是"等待"的意思,就是用于等待异步完成.并且await只能在async函数中使用; ...

  10. python--序列化:json、pickle、shelva

    序列化 数据类型--->str 一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀. 反序列化 str--->数据类型 序列化三大方法:json *** ...