如何实现最后执行zig操作的splay树,而不是第一个?

时间:2010-05-18 23:14:46

标签: haskell functional-programming splay-tree

对于我的算法& Data Structures类,我的任务是在Haskell中实现一个splay树。我的splay操作算法如下:

  1. 如果要显示的节点是根,则返回未更改的树。
  2. 如果要显示的节点是从根开始的一个级别,则执行zig操作并返回结果树。
  3. 如果要显示的节点是从根开始的两个或更多级别,则对从该节点开始的子树展开的结果执行zig-zig或Zig-zag操作,并返回结果树。
  4. 根据我老师的说法,这是有效的。但是,the Wikipedia description of a splay tree表示zig步骤“将仅作为展开操作的最后一步”,而在我的算法中,它是展开操作的第一步。

    我想实现一个splay树,它最后执行zig操作而不是第一个,但我不确定如何最好地完成它。在我看来,这样的算法会变得更加复杂,看看在确定是否应该执行zig操作之前,需要如何找到要展开的节点。

    如何在Haskell(或其他一些函数式语言)中实现它?

    实施例

    在这个例子中,我们搜索值4,提示我们将它展开到树的顶部。

    我的算法(zig作为第一步)

    1             1                   4
     \             \                 /
      2      zig    2    zig-zig    2
       \     -->     \   ------>   / \
        3             4           1   3
         \           /
          4         3
    

    维基百科算法(zig作为最后一步)

    1                   1           4
     \                   \         /
      2      zig-zig      4  zig  1
       \     ------>     /   -->   \
        3               3           3
         \             /           /
          4           2           2
    

    两棵树都有效,但它们的结构不同。我想用函数式语言实现第二个,最好是Haskell。

3 个答案:

答案 0 :(得分:3)

关键是要构建一个要显示的值的路径,然后从底部重建树,如果可能,一次重建两个级别(以便可以进行zig-zip与zig-zag的确定):

data Tree a = Empty | Node a (Tree a) (Tree a)
    deriving (Eq, Show)

data Direction = LH | RH
    deriving (Eq, Show)


splay :: (Ord a) => a -> Tree a -> Tree a
splay a t = rebuild $ path a t [(undefined,t)]
    where path a Empty ps = ps
          path a n@(Node b l r) ps =
              case compare a b of
                  EQ -> ps
                  LT -> path a l $ (LH, l) : ps
                  GT -> path a r $ (RH, r) : ps

          rebuild :: (Ord a) => [(Direction,Tree a)] -> Tree a
          rebuild ((_,n):[]) = n
          rebuild ((LH,x):(_,p):[]) = zigL x p
          rebuild ((RH,x):(_,p):[]) = zigR x p
          rebuild ((LH,x):(LH,p):(z,g):ps) = rebuild $ (z, zigzigL x p g):ps
          rebuild ((RH,x):(RH,p):(z,g):ps) = rebuild $ (z, zigzigR x p g):ps
          rebuild ((RH,x):(LH,p):(z,g):ps) = rebuild $ (z, zigzagL x p g):ps
          rebuild ((LH,x):(RH,p):(z,g):ps) = rebuild $ (z, zigzagR x p g):ps

          zigL (Node x a b) (Node p _ c) = Node x a (Node p b c)
          zigR (Node x a b) (Node p c _) = Node x (Node p c a) b

          zigzigL (Node x a b) (Node p _ c) (Node g _ d) =
              Node x a (Node p b (Node g c d))

          zigzigR (Node x a b) (Node p c _) (Node g d _) =
              Node x (Node p (Node g d c) a) b

          zigzagL (Node x b c) (Node p a _) (Node g _ d) =
              Node x (Node p a b) (Node g c d)

          zigzagR (Node x b c) (Node p _ a) (Node g d _) =
              Node x (Node g d b) (Node p c a)

您可以在我的repo中找到此代码,以及可运行的单元测试和快速检查。

答案 1 :(得分:0)

您确定正确阅读维基百科说明吗?有三种步骤:“zig”,“zig-zig”和“zig-zag”。 “zig”步骤是定义的,只有当x是根的子节点时才会发生。尽管名称不同,“zig-zig”和“Zig-zag”步骤没有“zig”步骤作为第一个组件。

在我看来,您的实施遵循维基百科在这方面的描述。

答案 2 :(得分:0)

您可以参考this course,其中包含一个非常好的演讲笔记,其中包含OCaml for Splay树中的代码。