目录
Part Ⅰ深度为h的满k叉树
性质
Part Ⅱ 区别各种树
总结
一、二叉搜索树(二叉排序树,二叉查找树,B树)
1.查找
2.插入
3.删除
二、平衡二叉树(AVL树)
三、B-树
四、B+树
五、B*树
六、红黑树
1.
一个深度为h的满k叉树有如下性质:第h层上的结点都是叶子结点,其余各层上每个结点都有k棵非空子树。如果按层次顺序(同层自左至右)从1开始对全部结点编号,问:
(1)各层的结点数目是多少?
(2)编号为i结点的双亲结点(若存在)的编号是多少?
(3)编号为i的结点的第j个孩子结点(若存在)的编号是多少?
(4)编号为i的结点有右兄弟的条件是什么?其右兄弟的编号是多少?
分析:
(1)
第0层有k 的0次方=1个结点,编号为1
第1层有k 的1次方=k个结点,编号为2 ->(k+1)
第2层有k 的2次方=kk个结点, 编号为k+2 ->(kk+k+1)
第3层有k 的3次方=kkk个结点, 编号为kk+k+2 ->(kkk+kk+k+1)
第h-1层有k 的h-1次方个结点, 编号为k^(h-1)+…+kk+k+2 ->(k^(h-2)+…+kk+k+1)
前h-1层节点个数:1*(1-k^x)/(1-k)
(2) 编号为1的结点是根结点,根节点没有父结点,编号为p(p!=1)的父结点是:int_down()
如果最左孩子编号为p,最右孩子编号为p+k-1,最右孩子减去根结点再除以k,就能得到最右孩子所在的组,该组即父结点的编号。
(3) i结点的第j个孩子结点(若存在)的编号:(i-1)*k+j+1
(4) 当(i-1)%k!=0时,结点i有右兄弟,编号为i+1
满k叉树编号为 i 的节点的孩子编号公式推导
结论:
满k叉树编号为i的节点第一个孩子的编号 j 满足推导过程:
设:节点 i 处在该 m 叉树的第 h层, (h = 1, 2, 3...)显然 i 是第 h 层的 个节点, 即 节点i 有个左兄弟
故节点i 的第一个孩子 j 有 个左兄弟
由此可得 j在第h + 1 层中的位置为
那么 节点j 在整棵满 k叉树的编号为
整理可得:
2.已知一棵深度为k的树中有n1个度为1的结点,n2个度为2的结点,…,nk个度为k的结点,问该树中有多少个叶子结点?
答:
设度为0的结点数(即叶子结点)为,则
结点关系:
分支关系:
联立求出叶子结点数为
3.已知在一棵含有n个结点的树中,只有度为k的分支结点和度为0的叶子结点。试求该树含有的叶子结点的数目。
答:
结点关系:
分支关系:
联立求出叶子结点数为:
4.证明:一棵满k叉树上的叶子结点数n0和非叶子结点数n1之间满足以下关系:n0=(k-1)n1+1。
答:
结点关系:
分支关系:
联立可证。
5.试分别推导含有n个结点和含n0个叶子结点的完全三叉树的深度H。
答:
n个结点的情况:前H-1层的结点数为:,前H层的结点数为:,所以
当k=3时,,解出
n0个叶子结点的情况:由题4知:,故,得出结果:
参考网址:https://blog.csdn.net/q354636996/article/details/89394595
B树 | 二叉树,每个结点只存储一个关键字,等于则命中,小于走左结点,大于走右结点; |
B-树 | 多路搜索树,每个结点存储M/2到M个关键字,非叶子结点存储指向关键字范围的子结点;所有关键字在整颗树中出现,且只出现一次,非叶子结点可以命中; |
B+树 | 在B-树基础上,为叶子结点增加链表指针,所有关键字都在叶子结点中出现,非叶子结点作为叶子结点的索引;B+树总是到叶子结点才命中; |
B*树 | 在B+树基础上,为非叶子结点也增加链表指针,将结点的最低利用率从1/2提高到2/3; |
定义:
空树或者是具有下列性质的二叉树:
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉排序树。
/* 二叉树的二叉链表结点结构定义 */
typedef struct BiTNode /* 结点结构 */
{
int data; /* 结点数据 */
struct BiTNode *lchild, *rchild; /* 左右孩子指针 */
} BiTNode, *BiTree;
在二叉排序树b中查找x的过程为:
1.若b是空树,则搜索失败,否则:
2.若x等于b的根节点的数据域之值,则查找成功;否则
3.若x小于b的根节点的数据域之值,则搜索左子树;否则
4.查找右子树
/* 递归查找二叉排序树T中是否存在key, */
/* 指针f指向T的双亲,其初始调用值为NULL */
/* 若查找成功,则指针p指向该数据元素结点,并返回TRUE */
/* 否则指针p指向查找路径上访问的最后一个结点并返回FALSE */
Status SearchBST(BiTree T, int key, BiTree f, BiTree *p)
{
if (!T) /* 查找不成功 */
{
*p = f;
return FALSE;
}
else if (key==T->data) /* 查找成功 */
{
*p = T;
return TRUE;
}
else if (keydata)
return SearchBST(T->lchild, key, T, p); /* 在左子树中继续查找 */
else
return SearchBST(T->rchild, key, T, p); /* 在右子树中继续查找 */
}
利用查找函数,将关键字放到树中的合适位置。
/* 当二叉排序树T中不存在关键字等于key的数据元素时, */
/* 插入key并返回TRUE,否则返回FALSE */
Status InsertBST(BiTree *T, int key)
{
BiTree p,s;
if (!SearchBST(*T, key, NULL, &p)) /* 查找不成功 */
{
s = (BiTree)malloc(sizeof(BiTNode));
s->data = key;
s->lchild = s->rchild = NULL;
if (!p)
*T = s; /* 插入s为新的根结点 */
else if (keydata)
p->lchild = s; /* 插入s为左孩子 */
else
p->rchild = s; /* 插入s为右孩子 */
return TRUE;
}
else
return FALSE; /* 树中已有关键字相同的结点,不再插入 */
}
在二叉排序树中删去一个结点,分三种情况讨论:
若*p结点为叶子结点,即PL(左子树)和PR(右子树)均为空树。由于删去叶子结点不破坏整棵树的结构,则只需修改其双亲结点的指针即可。
若p结点只有左子树PL或右子树PR,此时只要令PL或PR直接成为其双亲结点f的左子树(当p是左子树)或右子树(当p是右子树)即可,作此修改也不破坏二叉排序树的特性。
若p结点的左子树和右子树均不空。在删去p之后,为保持其它元素之间的相对位置不变,可按中序遍历保持有序进行调整。比较好的做法是,找到p的直接前驱(或直接后继)s,用s来替换结点p,然后再删除结点*s。
/* 若二叉排序树T中存在关键字等于key的数据元素时,则删除该数据元素结点, */
/* 并返回TRUE;否则返回FALSE。 */
Status DeleteBST(BiTree *T,int key)
{
if(!*T) /* 不存在关键字等于key的数据元素 */
return FALSE;
else
{
if (key==(*T)->data) /* 找到关键字等于key的数据元素 */
return Delete(T);
else if (key<(*T)->data)
return DeleteBST(&(*T)->lchild,key);
else
return DeleteBST(&(*T)->rchild,key);
}
}
/* 从二叉排序树中删除结点p,并重接它的左或右子树。 */
Status Delete(BiTree *p)
{
BiTree q,s;
if((*p)->rchild==NULL) /* 右子树空则只需重接它的左子树(待删结点是叶子也走此分支) */
{
q=*p; *p=(*p)->lchild; free(q);
}
else if((*p)->lchild==NULL) /* 只需重接它的右子树 */
{
q=*p; *p=(*p)->rchild; free(q);
}
else /* 左右子树均不空 */
{
q=*p; s=(*p)->lchild;
while(s->rchild) /* 转左,然后向右到尽头(找待删结点的前驱) */
{
q=s;
s=s->rchild;
}
(*p)->data=s->data; /* s指向被删结点的直接前驱(将被删结点前驱的值取代被删结点的值) */
if(q!=*p)
q->rchild=s->lchild; /* 重接q的右子树 */
else
q->lchild=s->lchild; /* 重接q的左子树 */
free(s);
}
return TRUE;
}
性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
判断是否是AVL树:
/**
* Definition of TreeNode:
* class TreeNode {
* public:
* int val;
* TreeNode *left, *right;
* TreeNode(int val) {
* this->val = val;
* this->left = this->right = NULL;
* }
* }
*/
class Solution {
public:
int depth(TreeNode* root) { //求树的高度
if (root == NULL) {
return 0;
}
return 1 + max(depth(root->left),depth(root->right));
}
bool isBalanced(TreeNode *root) {
if (root == NULL) {
return true;
}
int l = depth(root->left);
int r = depth(root->right);
if (abs(l -r) > 1) {
return false;
}
return isBalanced(root->left) && isBalanced(root->right);
}
};
是一种平衡的多路查找树。
B-树主要应用在文件系统。为了将大型数据库文件存储在硬盘上,以减少访问硬盘次数为目的,在此提出了一种平衡多路查找树——B-树结构。由其性能分析可知它的检索效率是相当高的,为了提高 B-树性能’还有很多种B-树的变型,力图对B-树进行改进。
M阶的B-Tree满足以下条件:
- 定义任意非叶子结点最多只有M个儿子,且M>2;
- 根结点的儿子数为[2, M];
- 除根结点以外的非叶子结点的儿子数为[M/2, M];
- 每个结点存放至少M/2-1(取上整)和至多M-1个关键字;(至少2个关键字,保证树的深度不会太大);
- 非叶子结点的关键字个数=指向儿子的指针个数-1;
- 非叶子结点的关键字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];
- 非叶子结点的指针:P[1], P[2], …, P[M];其中P[1]指向关键字小于K[1]的子树,P[M]指向关键字大于K[M-1]的子树,其它P[i]指向关键字属于(K[i-1], K[i])的子树;
- 所有叶子结点位于同一层,并且不带任何信息(可以看作是外部节点查找失败的节点,为空);
M = 3时:
B-树的特性:
- 关键字集合分布在整颗树中;
- 任何一个关键字出现且只出现在一个结点中(没有重复的关键字);
- 搜索有可能在非叶子结点结束;
- 其搜索性能等价于在关键字全集内做一次二分查找;
- 自动层次控制;
B+树是B-树的变体,也是一种多路搜索树。
其定义基本与B-树同,除了:
- 非叶子结点的子树指针与关键字个数相同;
- 非叶子结点的子树指针P[i],指向关键字值属于[K[i], K[i+1])的子树(B-树是开区间);
- 为所有叶子结点增加一个链指针;
- 所有关键字都在叶子结点出现;
M = 3时:
B+的搜索与B-树也基本相同,区别是B+树只有达到叶子结点才命中(B-树可以在非叶子结点命中),其性能也等价于在关键字全集做一次二分查找;
B+的特性:
- 所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好是有序的;
- 不可能在非叶子结点命中;
- 非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层;
- 更适合文件索引系
数据库为什么使用B+树?
是B+树的变体,在B+树的非根和非叶子结点再增加指向兄弟的指针;
定义:红黑树是一棵二叉查找树,但它在二叉查找树的基础上增加了着色和相关的性质使得红黑树相对平衡,从而保证了红黑树的查找、插入、删除的时间复杂度最坏为O(log n)。
性质:
- 每个结点要么是红的要么是黑的。
- 根结点是黑的。
- 每个叶子结点(叶结点即指树尾端NIL指针或NULL结点)都是黑的。
- 如果一个结点是红的,那么它的两个儿子都是黑的。
- 对于任意结点而言,其到叶结点树尾端NIL指针的每条路径都包含相同数目的黑结点。
参考网址
https://blog.csdn.net/lis_12/article/details/57416576
http://www.cnblogs.com/oldhorse/archive/2009/11/16/1604009.html
http://blog.csdn.net/v_JULY_v/article/details/6105630
http://blog.csdn.net/hguisu/article/details/7786014