二叉搜索树
二叉搜索树跟之前讲到的由二分引出树的表示有所关联,比如上图画圈的那棵查找树就是1~11进行二分查找的过程,因此查找树左右根之间的关系就是左<根<右,且右子树的任意元素都大于左子树。
构建二叉搜索树的代码:
void insert(Tree*tree,int value)
{
Node *node=(Node *)malloc(sizeof(Node));//申请新节点以插入
node->data=value;//赋权值
node->left=NULL;//左右儿子为空
node->right=NULL;
if (tree->root==NULL)//为空树
{
tree->root=node;//新插入的节点为根
}
else//已有元素
{
Node*tmp=tree->root;
while (tmp!=NULL)//对新插入元素进行比较直到找到合适的位置或到根节点为止
{
if (value<tmp->data)
{
if(tmp->left==NULL)
{
tmp->left=node;
return;
}
else
{
tmp=tmp->left;
}
}
else
{
if(tmp->right==NULL)
{
tmp->right=node;
return;
}
else
{
tmp=tmp->right;
}
}
}
}
}
在对二叉搜索树进行遍历时平均复杂度为O(logn),但当左子树与右子树差距太大时,复杂度会大大超过,这时我们就需要调整树的形态为平衡二叉树。
平衡二叉树
当一棵搜索二叉树的BF<=1时我们就可以称他为平衡二叉树了(平衡二叉树必须时二叉搜索树)。平衡二叉树的查找、插入、删除操作在平均和最坏的情况下都是O(logn),这得益于它时刻维护着二叉树的平衡。如果我们需要查找的集合本身没有顺序,在频繁查找的同时也经常的插入和删除,平衡二叉树树是不错的选择。不平衡的二叉查找树在查找时的效率是很低的,因此,AVL如何维护二叉树的平衡是我们的学习重点。
AVL树的平衡调整
定义平衡二叉树节点结构:
typedef struct Node
{
int key;
struct Node *left;
struct Node *right;
int height;
}BTNode;
平衡调整过程是通过在一棵平衡二叉树中依次插入元素(按照二叉排序树的方式),若出现不平衡,则要根据新插入的结点与最低不平衡结点的位置关系进行相应的调整。分为LL型、RR型、LR型和RL型4种类型。
RR型
我们以月份的字典序为例下图中的mar由于May,Nov的插入变成了不平衡点,要将他调整为平衡的我们将其进行右单旋操作,把May变成了根节点。
RR型调整的一般形式如上图所示,表示在A的右孩子B的右子树BR(不一定为空)中插入结点(图中阴影部分所示)而导致不平衡( h 表示子树的深度)。这种情况调整如下:
将A的右孩子B提升为新的根结点
将原来的根结点A降为B的左孩子
各子树按大小关系连接(AL和BR不变,BL调整为A的右子树)。
代码实现:
BTNode *RR(struct Node *y)
{
BTNode *x = y->right;
y->right = x->left;
x->left = y;
y->height = max(height(y->left), height(y->right)) + 1;
x->height = max(height(x->left), height(x->right)) + 1;
return x;
}
LL型依此类推即可
LR型
LR型调整的一般形式如上所示,表示在A的左孩子B的右子树(根结点为C,不一定为空)中插入结点(图中两个阴影部分之一)而导致不平衡( h 表示子树的深度)。这种情况调整如下:①将B的左孩子C提升为新的根结点;②将原来的根结点A降为C的右孩子;③各子树按大小关系连接(BL和AR不变,CL和CR分别调整为B的右子树和A的左子树)。
代码实现
BTNode* LR(BTNode* y)
{
BTNode* x = y->left;
y->left = rr_rotate(x);
return LL(y);
}
RL型类似