再解 fhq

  • 前几天做 P3850 [TJOI2007] 书架 时初步学习了按大小分裂,但是左想右想想不明白:显然按大小分裂时,整棵树的形态已经与 val 值无关了,那么树的形态应当是由随机的 key 值来决定,那么我凭什么能确定查询的值一定是正确的呢?当时我感到 split 和 merge 操作中一定有什么性质是我漏掉的,昨晚有些睡不着,干脆把这件事想明白了。
  • 这里直接说结论:根据我们之前对 split 和 merge 操作的理解,我们可以发现,这两个操作可以保证树整体的中序遍历顺序不变。举个例子:假如我当前的树的中序遍历顺序是 ABCDEFG,那么不管是经过按值分裂还是按大小分裂,假如是从 D 分裂,那么左树的中序遍历就是 ABCD,右树则是 EFG;此时我新建一个节点 H,将左树和这个新节点合并,那么不管新节点的 key 值是多少,插入后的左树的中序遍历一定是 ABCDH,同理,再将此时的左右树合并,得到的新树的中序遍历就是 ABCDHEFG。
  • 那么这是为什么呢?我们结合这两个操作的过程进行理解:
    • 首先是 split:不管是按值分裂还是按大小分裂,遵循的逻辑都是:只要满足挂在左树的条件,就将当前节点挂在左树,然后将当前节点的右子节点当作左树的空位,之后向右子节点遍历,反之同理(后文以挂左树为例分析,右树可以等价)。在这种操作逻辑下,把当前节点挂到左树上时,是直接把当前节点和其左子树挂到左树上,之后向右遍历,再逐渐将右边的节点及其左子树挂到左树的空位上,直到满足条件为止。那么我们可以看出,由于向左树挂节点是直接挂当前节点和其左子树,所以分裂后中序遍历的左边一定是不会变的;当我们需要把当前节点右边的节点挂到左树上时,首先挂的也是节点和其左子树,也就是中序遍历时最先遍历到的点,所以是不会改变整体的中序遍历的顺序的。
    • 接下来是 merge:重点理解一个事情:key 值不会对合并后的中序遍历顺序产生任何影响。我们依然以左树为主进行分析,可以发现右树的某棵子树接到左树上只可能是子树的根节点成为左树某个节点的右儿子,即使是插入左树的两个节点 A、B 之间,也不会打乱顺序,如图所示:
    • 进行到这里我们可以思考一下,保证中序遍历的顺序不变的底层逻辑是什么?其实就在于往树上挂节点的方式,不管是 split 还是 merge,都是左树只挂右节点,右树只挂左节点。我们依然拿左树来举例,这种挂法首先保证了被挂节点 u 的左子树的形态不会变化;其次考虑右子树,由于只往右儿子上挂节点,所以 u 的右子树原本就有的节点的顺序不会有变化,即使是有穿插的情况,这种挂法也保证了一定是将右树最先遍历到的部分接到左树最后遍历到的部分,所以整体的顺序仍然不变。

posted on 2025-02-26 09:32  wuhu12345  阅读(17)  评论(0)    收藏  举报

导航