《初学者视角:基于公理系统的AVL版RBT 融合树推导与性能极限》

31 阅读6分钟

前言:尽管只是闭门造车学习了两个多月Java的小白,在我依据公理系统6天时间深度思考下的红黑树而言,现在已经达到了我的认知极限了,目前在我的认知下这个就是RBT的最优解了。 最初的视角:最开始因为刚基于公理写完AVL,我的视野还停留在AVL的框架没有出来,所以最初的设想是也用AVL的增删统一入口的视角去写RBT的失调调整,但是当我第一天就把put的工程化落地代码写完以后(源码前面文章有),我就意识到了这个想法是错误的。因为添加只是局部微调就可以实现平衡,而删除是以黑高为基准实现,两套逻辑在数学公理面前压根不可能共存。然后我就需要调整策略怎么应付损失的黑高,马上我就找到了突破口,因为我的BST是后继删跟真删的组合,这样的话天然就会有一遍是空子节点。我就可以直接在当前层把损失的黑高补回(后继删的顶替节点)或者在被真删节点的父节点处把损失补回,这样我的失衡调整就已经天然的形成了一道防火墙。所以能进入失衡调整的节点必定是黑节点。不过当时把当前节点的失衡给漏了想用前驱的方式进行失衡调整,因为我没有看过源码当时误以为作者的策略是以父节点为锚点调整,而我是以爷爷节点为锚点的方式进行失衡调整,因为我对失衡调整的策略是兄弟节点能补偿就补偿,不能补偿就变红上报的策略,所以到了根节点层时必然会形成无法闭环,导致我困惑了很多天,并且也意识到了我以爷爷节点为锚点的视角天然就是错的,因为当前节点并没有进行失衡调整就把锅甩给父亲了。 中期视角:搞了2天时间我被我的旋转策略彻底卡死住了在根节点层的闭环问题,期间想了各种单独处理根节点的特殊情况的问题,导致进入了死胡同视角出不来了。实在顶不住这个压力了以后,我选择了妥协(学习数据结构1个多月的时间我一直不屑于去看任何源码及思路,包括AI的分析源码及思路)问了元宝作者的的失衡循环源码是怎么样的,当时元宝给我的也都是封装好了的一些函数,但是我却能清楚的看懂作者的这些封装内部到底是在做什么,当时看到那个源码以后我甚至释然的笑了,原来大神的代码也不过如此。跟我写的代码几乎没有任何实质性的区别,仅仅只是把指针的操作以及颜色的判断写成了一个方法而已,甚至源码的颜色设置有些都是多余的废话。源码的结论:把兄弟节点损失的黑高甩锅给她的兄弟节点,让兄弟节点也跟着损失黑高来补偿损失当前节点损失的黑高,然后一直向上甩锅甩到根节点效率log(n)(第一次用这个函数,高中数学早忘了)。通过对源码的分析我彻底意识到了我问题在哪了。 后期视角:当我意识到我在当前层解决损失黑高问题的策略当到达root的时候必然会造成root不知道到底哪边失衡时,我想到了用根节点的左右路径黑子的数量来作为判断依据,当我写完这个判断依据的函数发给元宝时,被她劈头盖脸的臭骂了一顿,说我是在不按数学公理编程,会导致结构性问题时,我一直在骂她,骂了好久,说她根本没有理解我的策略到底是在干什么。最终我在基于我的数学公理编程的理念下彻底理解了元宝的意图,并且接受了她这“神之一手”的设计。(第一次接受AI提供的思路编程)元宝告诉我应该专门设计一个bh字段来记录每个节点的黑高,就像AVL一样动态保存。这样我写代码就压根不需要去用前驱来引导循环了,直接用bh字段来设计一个黑高平衡因子,来判断当前节点是哪边失衡,从而做到回退到根节点时也依然能拿到失衡的方向从而进行失衡调整,这样就一定能实现逻辑的闭环问题。 总结:刚开始想通这个的时候,其实还挺兴奋的,以为自己发现了什么新大陆,但是冷静思考过后我就意识到了这个容器已经用了60多年了,绝对已经早就有工程化的落地代码以及理论基础了绝不是什么新的发现。我就找元宝验证,结果果然不出我所料,虽然有点小失落,但是也并不会对我有特别大的影响,至少我能从数学公理把红黑树推到这一步并且我还是个行业小白而言,这也是相当炸裂的事情。至于代码的落地也只是时间的问题了,关于旋转跟变色的一些细节,对于能看懂我这篇文章的人应该压根不需要我解释怎么旋转以及变色了。最终AVL-RBT 融合树的逻辑闭环,曾经横亘在眼前的 B 树与 B+ 树,也不再是模糊的大山。剥去其复杂的表象,B 树的内部结构在我眼中不过是一个存储 Key 的有序数组,以及基于该数组的分裂与合并逻辑。曾经需要仰望的数据结构,如今我已经清晰的站到她的山脚下。这便是公理系统推导给予我的最大馈赠:不再畏惧任何形态的树。 补充:我的节点自治好像也需要一直收敛到根才能真正的实现平衡。假如某个路径退到根的路径也补偿不了时,最终还是需要root兜底处理。还漏了个最重要的点,哪怕我的防火墙处理好了失衡调整同样需要进循环去刷一遍存在感bh更新,这个是核心。