自底向上伸展树(之字形旋转+一字形旋转)

【0】README

0.1) 本文总结于 数据结构与算法分析,核心剖析路线为原创, 旨在理清 自底向上伸展树(之字形旋转+一字形旋转) 的基本思路;
0.2) 自底向上伸展树 是基于 AVL树,for detailed AVL, please visit http://blog.csdn.net/pacosonswjtu/article/details/50522677 ;
0.2) 对于伸展树的实现而言, 自顶向下伸展树只用到了O(1)的额外空间,且能够保持O(logN)的摊还时间界,通常推荐自顶向下伸展树的应用;for detailed top-down splay tree, please visit http://blog.csdn.net/pacosonswjtu/article/details/50609414 (干货——推荐自顶向下伸展树的应用)

【1】伸展树(之字形旋转+一字形旋转)

1.1)定义:

  • 伸展树保证从空树开始任意连续M次对树的操作最多花费 O(M logN)时间;

1.2)摊还运行时间:

  • 当 M 次操作的序列总的最坏情形运行时间为 O(MF(N))时,我们就说它的摊还运行时间为 O(F(N)), 因此一颗伸展树 每次操作的摊还代价是 O(log N);(摊还 == 分期偿还)

1.3)如果任意特定操作可以有最坏时间界O(N), 而我们仍然要求一个 O(logN)的摊还时间界, 那么很清楚, 只要一个节点被访问, 它就必须被移动。 否则,一旦我们发现一个深层节点,我们就有可能不断对它进行find 操作。 如果这个节点不改变位置, 而每次访问又花费 O(N), 那么M次访问将花费 O(M * N)的时间;
1.4)引入伸展树的原因:

  • 如果任意特定操作可以有最坏时间界O(N), 而我们仍然要求一个 O(logN)的时间复杂度, 显然,为了达到这个目标,只要一个节点被访问了,那么它必须被移动。否则,一旦我们发现一个深层节点,我们就有可能不断对它进行find操作。 如果这个节点不改变位置, 而每次访问又花费了 O(N), 那么M次访问将花费O(MN)的时间; (干货——引入伸展树的原因)

1.5)伸展树的基本想法: (干货——伸展树的基本想法)

  • 1.5.1)当一个节点被访问后, 它就要经过一系列AVL 树的旋转被放到根上。注意, 如果一个节点很深,那么在其路径上就存在许多的节点也相对较深,通过重新构造可以使对所有这些节点的进一步访问所花费的时间变少。
  • 1.5.2)因此,如果节点过深,那么我们还要求重新构造应该具有平衡这棵树(到某种程度)的作用;除了在理论上给出好的时间界外, 这种方法还可能有实际的效用(reasons);
    • r1)因为在许多应用中当一个节点被访问时, 它就很可能不久后再被访问到;
    • r2)另外, 伸展树还不要求保留高度或平衡信息, 因此它在某种程度上节省空间并简化代码;

【2】一个简单的想法(不过行不通)

2.1)执行单旋转:实施上面描述的重新构造的一种方法是执行单旋转,从下到上进行;
2.2)不过这种方法效率不是很高:因为这些旋转的效果是将 k1 一直推向树根,使得对 k1 的进一步访问很容易,不足的是它把另外一个节点 k3 几乎推向和k1以前同样的深度;
2.3)结论(这个想法还不够好):虽然这个策略使得对 k1 的访问花费时间减少,但是它并没有明显的改变访问路径上其他节点的状况;

【3】展开(因为章节2中的方法行不通,所以才有伸展树的展开)

我们仍然是从底部向上沿着访问 路径旋转;
3.1)令X 是在访问路径上的一个节点, 我们将在这个路径上实施旋转操作。

  • 3.1.1)如果X 的父节点是树根, 那么我们只要旋转X 和树根;(这是沿着路径上的最后的旋转)(干货——单旋转定义)
  • 3.1.2)否则, X就有父亲P 和 祖父G, 存在两种情况需要考虑:

    • case1)第一种情况是 之字形:我们就执行一次AVL 那样的双旋转;(访问节点X是P的右儿子, 而父亲P是G的左儿子的形式;或者访问节点X是P的左儿子, 而父亲P是G的右儿子的形式)(干货——之字形旋转情形的定义,即访问节点X 介于父节点P 和 祖父节点G 之间,确切地说是 P≤X≤G)
      自底向上伸展树(之字形旋转+一字形旋转)_第1张图片

    • case2)第二种情况是 一字形:我们就把左边的树变成右边的树;(访问节点X 和 父亲P 都分别是父亲P和祖父G的左儿子,或者都是右儿子) (干货——一字形旋转情形的定义 ,即访问节点X 不介于父节点P 和 祖父节点G 之间,确切地说是 X≤P≤G 或 G≤P≤X)

3.2)对展开操作的分析(Analysis)

  • A1)展开操作不仅将访问 的节点移动到根处,而且还有把访问路径上的大部分节点的深度大致减少一半的效果(某些浅的节点最多向下推后两个层次)
  • A2)下图中指出 关键字为1的节点展开的结果 。区别在于:在对关键字1的节点访问(花费N-1个单元的时间)之后,对关键字为2的节点的访问只花费 N/2 个时间单元而不是 N - 2个时间单元;

你可能感兴趣的:(数据结构)