平衡二叉树AVL

很多时候学习数据结构的时候,刚开始觉得还可以接受,到了树和图的话,就慢慢劝退了。很多时候我们觉得这个东西很难,但是实际情况呢,嗯。。它真的挺难的,但是因为它难,学习上没有及时的收获感,导致我们学而止步了。但是呢,有阻力才代表我们在上升。王强老师之前在演讲的时候说到一个例子,形式逻辑的三段论来验证他可以学好计算机,大前提是计算机是人发明的东西,小前提是人都应该懂人发明的东西,王强搞不懂计算机,结论:王强肯定不是人。虽然不是真理哈,但是也表明了我们对于学习的态度问题,既然难,说明这个内容它是包含了更多前人的智慧,老一辈花了几年几十年总结出来的内容,我们如果要求自己一两遍就弄懂,其实也不太现实(排除特别优秀的同学),所以要多给自己一点耐心,没弄懂,不是自己没有天赋,而是自己花的时间还不够多。

AVL树的难点在于旋转问题,保证整棵树是平衡的,关于AVL树相关的概率这里不再重复,相关的数据都可以找到介绍。

旋转分为四种情况:

  1. 左旋

  2. 右旋

  3. 先左旋后右旋

  4. 先右旋后左旋

代码实现

结点

// 定义结点,内容很清晰,结点值,左右结点指针
struct node {
    int val;
    struct node *left, *right;
};

左旋

左旋图示:

左旋代码:

右旋

右旋图示:

右旋代码:

先左旋后右旋

图示:

代码:

先右旋后左旋

图示:

代码:

我们可以看到,在对于旋转的代码上实现是比较明确的,结合图例很好理解,难点在于我们插入的时候根据什么来判断要进行何种旋转,下面我们来看下插入代码的实现:

插入代码

这里的核心代码是判断什么时候进行何种旋转的代码,总结:

  • 插入到左子树

    • 插入值小于最小子树root结点的左孩子的值,只需要右旋

    • 插入值大于最小子树root结点的左孩子的值,先左旋有右旋

  • 插入到右子树

    • 插入值大于最小子树root结点的左孩子的值,只需要左旋

    • 插入值小于最小子树root结点的左孩子的值,先右旋后左旋

可以看到,如果插入是左子树,那么最终一定是要右旋;插入是右子树,一定最后是左旋。这就很好记了。

这里提到了最小子树,那么是什么意思呢,我们上图的图示中只画了三个结点的树,实际情况中,树的规模肯定不止三个结点,那么最小子树就是要找到插入结点后,导致不平衡的那三个结点构成的最小子树。

总结简记:

插左树,大于左,则右旋,小于左,先左后右;

插右树,小于右,则左旋,大于右,先右后左;

以上,我们则可以完美地拿下AVL树了,如果还不是很明白,那就多找几篇文章来看下吧,多花点时间琢磨琢磨,一定可以把AVL树学明白的!

绘图文件,请参考这里。使用的是draw.io平台,可以直接导入该文件。

Reference

参考代码来源:https://github.com/liuchuo/PAT/blob/master/AdvancedLevel_C%2B%2B/1066.%20Root%20of%20AVL%20Tree%20(25).cpp

Last updated

Was this helpful?