转自:AVL树(一)之 图文解析 和 C语言的实现(本文图片及文字描述部分转自该文)
参考:邓俊辉 的数据结构,部分图片来自该资料代码是C#写的
AVL树是根据它的发明者G.M. Adelson-Velsky和E.M. Landis命名的。
它是最先发明的自平衡二叉查找树,也被称为高度平衡树。相比于"二叉查找树",它的特点是:AVL树中任何节点的两个子树的高度最大差别为1。(树的高度:树中结点的最大层次)
上面的两张图片,左边的是AVL树,它的任何节点的两个子树的高度差别都<=1;而右边的不是AVL树,因为7的两颗子树的高度相差为2(以2为根节点的树的高度是3,而节点8的高度是1)。
AVL树的查找、插入和删除在平均和最坏情况下都是O(logn)。
如果在AVL树中插入或删除节点后,使得高度之差大于1。此时,AVL树的平衡状态就被破坏,它就不再是一棵二叉树;为了让它重新维持在一个平衡状态,就需要对其进行旋转处理。学AVL树,重点的地方也就是它的旋转算法
首先要明确的是,== 平衡二叉树是一棵二叉排序树,它的出现是为了解决普通二叉排序树(普通二叉排序树)不平衡的问题。如图,在插入结点之前首先要查找插入位置,假如要在5结点后插入,普通二叉排序树需要比较五次,而平衡二叉树只需要比较三次。假如结点规模进一步加大,效率提升也会更明显。
(图片来自https://blog.youkuaiyun.com/m0_38036210/article/details/100517125)

0X01 节点和树的定义
1. 节点的定义
public class Node
{
public int Key;
public Node Parent;//parent
public Node L; //left
public Node R; //right
public int H; //height;
public Node(int key, Node parent=null, Node left=null, Node right=null,int h=1)
{
Key = key;
Parent = parent;
L = left;
R = right;
H = h;
}
}
2. 树的定义
public class AVLTree
{
//树的根节点
public Node Root;
}
3.树的高度
空的二叉树的高度是0,非空树只有一个节点根节点高度为1等等
/// <summary>
/// 树的高度
/// 树的高度为最大层次。即空的二叉树的高度是0,非空树的高度等于它的最大层次(根的层次为1,根的子节点为第2层,依次类推),这里空树的高度取0,有的教材资料是-1
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
public int Height(Node a)
{
return a == null ? 0 : a.H;
}
public int MaxHeight(Node a, Node b)
{
return a == null ? (b == null ? 0 : b.H) : (b == null ? a.H : a.H > b.H ? a.H : b.H);
}
public int UpdateHeight(Node a)
{
a.H = MaxHeight(a.L, a.R) + 1;
return a.H;
}
/// <summary>
/// 是否平衡
/// </summary>
/// <param name="tree"></param>
/// <returns></returns>
public bool IsBalanced(Node tree)
{
return -2 < Height(tree.L) - Height(tree.R) && Height(tree.L) - Height(tree.R) < 2;
}
/// <summary>
/// 在左、右孩子中取更高者
/// </summary>
/// <param name="tree"></param>
/// <returns></returns>
public Node TallerChild(Node tree)
{
if (tree == null)
return null;
if (Height(tree.L) > Height(tree.R))
return tree.L;
else return tree.R;
}
0X02 单旋和双旋
前面说过,如果在AVL树中进行插入或删除节点后,可能导致AVL树失去平衡。
2.1 zag单旋(左旋)
如果说节点g失去平衡,g的右孩子p高度比左孩子高,且右孩子p的右孩子v高度比右孩子p的左孩子高度高,那么进行逆时针旋转进行调整高度

假设在子树v中插入某个节点x(虚线连接部分,其中一个节点对应x,另一个为空节点),这时候v节点是平衡的,p节点也是平衡,但是g节点不平衡,g的右孩子高度减去g的做孩子高度等于2,需要对g节点进行调整,这时候以g为轴进行逆时针旋转(左旋),调整后从a子树变为b子树;从而达到子树的平衡, 而且高度未变化,所以该子树平衡后,父节点及除子树的其他树的部分都是平衡的。
旋转代码
/// <summary>
/// zag 左旋转,逆时针旋转,单旋
/// (逆时针旋转g)
/// g
/// / \
/// T0 p
/// / \
/// T1 v
/// / \
/// T2 T3
///
/// zag单旋后:
/// p(b)
/// / \
/// g(a) v(c)
/// / \ / \
/// T0 T1 T2 T3
///
/// </summary>
/// <param name="tree">非空孙辈节点</param>
/// <returns>该树新的的根节点</returns>
public Node Zag(Node tree)
{
Node v = tree, p = v.Parent, g = p.Parent, r = g.Parent;
Node a = g, b = p, c = v;
Node T0 = g.L, T1 = p.L, T2 = v.L, T3 = v.R;
a.L = T0; if (T0 != null) T0.Parent = a;
a.R = T1; if (T1 != null) T1.Parent = a; UpdateHeight(a);
c.L = T2; if (T2 != null) T2.Parent = c;
c.R = T3; if (T3 != null) T3.Parent = c; UpdateHeight(c);
b.L = a; a.Parent = b;
b.R = c; c.Parent = b; UpdateHeight(b);
Connect

本文深入探讨AVL树的基本概念、自平衡机制及其在C#中的具体实现,包括节点旋转、查找、插入与删除操作。通过实例演示AVL树如何保持平衡,提高搜索效率。

最低0.47元/天 解锁文章
235

被折叠的 条评论
为什么被折叠?



