树的应用

二叉排序树

二叉排序树,也称二叉查找树,二叉搜索树,或BST。二叉排序树或者为一个空树,或者为一个满足下列特性的非空二叉树:
1)若左子树非空,则左子树上所有结点关键字值均小于根结点的关键字值。
2)若右子树非空,则右子树上所有结点关键字值均大于根结点的关键字值。
3)左、右子树本身也是一颗二叉排序树。
对二叉排序树进行中序遍历,可以得到一个递增的有序序列。
判断一颗二叉树是不是二叉排序树

int prev1 = INT_MIN;//定义为最小的整数
typedef struct node{
    int data;
    node *lchild, *rchild;
}BiTree;
int JudgeBST(BiTree *bt){
    int b1,b2;
    if(bt == NULL)
        return 1;
    else{
        b1 = JudgeBST(bt->lchild);
        if(b1 == 0 || prev1 >= bt->data)
            return 0;
        prev1 = bt->data;
        b2 = JudgeBST(bt->rchild);
        return b2;
    }
}

平衡二叉树

为了避免树的高度增长过快,降低二叉排序树的性能,我们规定在插入和删除二叉树结点时,要保证任意结点的左、右子树高度差的绝对值不超过1,并将这样的二叉树称为平衡二叉树(AVL)。定义结点左子树和右子树的高度差为该结点的平衡因子,则平衡二叉树结点的平衡因子的值只可能是0、-1或1.
判断一颗二叉树是不是平衡二叉树
思路1:在遍历树的每个节点的时候,调用函数tree_height得到他的左右子树的深度。如果每个节点的左右子树的深度相差都不超过1,按照定义它就是一颗平衡的二叉树。

struct BinaryTreeNode{
    int m_nValue;
    BinaryTreeNode* m_Left;
    BinaryTreeNode* m_Right;
};
int tree_heigth(BinaryTreeNode *bt){
    if(bt == NULL)
        return 0;
    else{
        left = tree_height(bt->left)+1;
        right = tree_height(bt->right)+1;
        return (left > right) ? left : right;
    }
}
bool IsBalanced(BinaryTreeNode* pRoot){
    if(pRoot == NULL)
        return true;
    int left = tree_height(pRoot->m_Left);
    int right = tree_height(pRoot->m_Right);
    int diff = left - right;
    if(diff > 1 || diff < -1)
        return false;
    return IsBalanced(pRoot->m_left) && IsBalanced(pRoot->m_Right);
}

这种方法虽然简洁,但是每一个节点都会被遍历多次,效率较低。
思路2:用后序遍历的方式遍历二叉树的每一个节点,在遍历到一个节点之前我们已经遍历了它的左右子树。只要在遍历每个节点的时候记录它的高度,就可以一边遍历一边判断每个节点是不是平衡的。

bool IsBalanced(BinaryTreeNode *pRoot, int *pDepth){
    if(pRoot == NULL){
        *pDepth = 0;
        return true;
    }
    int left, right;
    if(IsBalanced(pRoot->m_Left, &left) && IsBalanced(pRoot->m_Right, &right)){
        int diff = left - right;
        if(diff <= 1 && diff >= -1){
            *pDepth=1+(left > right ? left : right);
            return true;
        }
    }
    return false;
}

思路3:求出根节点的最大深度和最小深度,则最大深度与最小深度之差就是dis就是任以子树深度差最大值,所以只要dis小于等于1,此树就是平衡二叉树

int maxDepth(TreeNode* root){
    if(root == NULL)
        return 0;
    return 1 + max(maxDepth(root->left), maxDepth(root->right));
}
int minDepth(TreeNode* root){
    if(root == NULL){
        return 0;
    }
    return 1 + min(minDepth(root->left), minDepth(root->right));
}
bool isBalanced(TreeNode* root){
    return (maxDepth(root) - minDepth(root) <= 1);
}

红黑树

红黑树也是被广泛运用的平衡二叉搜索树,RB-tree要满足如下规则:
1.每个节点不是红色就是黑色
2.根节点为黑色
3.如果节点为红,其子节点比为黑
4.任一节点至NULL(树尾端)的任何路径,所含之黑节点数必须相同。
插入节点的四种情况
新节点为X,其父节点为P,祖父节点为G,伯父节点为S,曾祖父节点为GG。由于规则4,X比为红。
1.S为黑,且X为外侧插入的情况,我们先对P,G做一次单旋转,再更改P,G颜色
这里写图片描述
2.S为黑且X为内侧插入。对此情况,必须先对P,X做一次单旋转并更改G,X的颜色,再将结果做一次单旋转,即可再次满足红黑树规则3.
这里写图片描述
3.S为红且X为外侧插入。对此情况,先对P和G做一次单旋转,并改变X的颜色。此时如果GG为黑,一切搞定。但如果GG为红,见4
这里写图片描述
4.S为红且X为外侧插入。对此情况,先对P和G做一次单旋转,并改变X的颜色。此时如果GG亦为红,还得持续往上做,直到不再有父子连续为红的情况。
这里写图片描述

哈夫曼树及哈夫曼编码

1.哈夫曼树的构造

给定N各权值分别为w1,w2,…,wN的节点。构造哈夫曼树的算法如下:
1)将这N个结点分别作为N颗仅含一个结点的二叉树,构成森林F.
2)构造一个新阶段,并从F中选取两个颗根节点权值最小的树作为新节点的左、右子树
3)从F中删除刚才选出的两棵树,同时将新得到的树加入F中。
4)重复步骤2)3),直到F中只剩下一棵树为止。

2.哈夫曼编码

构造哈夫曼编码首先要构造一颗哈夫曼树。首先,将每个出现的字符当做一个独立的结点,其权值为它出现的频度,然后构造出对于的哈夫曼树。其中边标记为0表示“转向左孩子”,标记为1表示“转向右孩子”。树中所有叶节点的带权路径长度之和称为该树的带权路径长度,记为:
WPL= ∑wi*li(i=1…n)
wi是第i个叶节点所带的权值,li是该叶节点到根节点的路径长度。
利用哈夫曼树可以设计出总长度最短的二进制前缀编码。

B树

B+树

B+树由B树和索引顺序访问方法演化而来,B+树是为磁盘或其他直接存取辅助设备而设计的一种平衡查找树。在B+树中,所有记录节点都按键值得大小顺序存放在同一层的叶子节点,各叶子节点指针进行连接。

插入

B+树的插入必须保证插入后叶子节点中的记录依然排序,同时需要考虑插入到B+树的三种情况
1.Leaf Page不满,Index Page不满,直接将记录插入到叶子节点
例如:在此情况下,插入键值28
图一
2.Leaf Page满,Index Page不满,拆分Leaf Page,将中间的节点放入到Index Page中,小于中间节点的记录放左边,大于等于中间节点的记录放右边。
例如:在此情况下,插入键值70
图二
3.Leaf Page满,Index Page满,拆分Leaf Page,小于中间节点的记录放左边,大于等于中间节点的记录放右边,拆分Index Page,小于中间节点的记录放左边,大于中间节点的记录放右边,中间节点放入上一层Index Page
例如:在此情况下,插入键值95
图3

删除

B+树使用填充因子来控制树的删除编变化,50%是填充因子可设的最小值。B+树的删除操作必须保证删除后叶子节点中的记录依然排序,B+shu的删除根据填充因子的变化来衡量。
删除B+树的三种情况:
1.叶节点不小于填充因子,中间节点不小于填充因子,直接将记录从叶子节点删除,如果该节点还是Index Page的节点,用该节点的右节点代替。
2.叶子节点小于填充因子,中间节点不小于填充因子,合并叶子节点和它的兄弟节点,同时更新Index Page。
3.叶子节点小于填充因子,中间节点小于填充因子,合并叶子节点和它的兄弟节点,更新Index Page,合并Index Page和它的兄弟节点。

B+树索引

索引的特点

B+树索引是基于B+树发展而来的,然而其余基本的B+树数据结构又有所不同,这个区别如下表所示
这里写图片描述
通常来说,B+树索引用于基于磁盘的数据库系统中,即数据最后持久化放在磁盘上。每个叶子节点一般包含较多的记录,因此具有较高的扇出。这意味着在数据库中B+树的索引高度一般较小,在3-4层,而树的高度也决定了磁盘IO搜索的次数,从而影响数据库的整体性能。

猜你喜欢

转载自blog.csdn.net/xiaomimi1993/article/details/81476081