如何实现最后执行 zig 操作而不是首先执行 zig 操作的展开树?

发布于 2024-09-01 16:46:21 字数 1156 浏览 3 评论 0原文

对于我的算法和数据结构课上,我的任务是在 Haskell 中实现展开树。我的展开操作的算法如下:

  1. 如果要展开的节点是根,则返回未改变的树。
  2. 如果要展开的节点距根仅一级,则执行 zig 操作并返回结果树。
  3. 如果要展开的节点距离根有两级或以上,则对从该节点开始展开子树的结果执行 zig-zig 或 zig-zag 操作,并返回结果树。

根据我的老师的说法,这是有效的。然而,维基百科对展开树的描述表示 zig 步骤“仅作为最后一个步骤完成”展开操作中的一步”,而在我的算法中,这是展开操作的第一步。

我想实现一个展开树,它最后而不是第一个执行 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)实现第二个。

For my Algorithms & Data Structures class, I've been tasked with implementing a splay tree in Haskell. My algorithm for the splay operation is as follows:

  1. If the node to be splayed is the root, the unaltered tree is returned.
  2. If the node to be splayed is one level from the root, a zig operation is performed and the resulting tree is returned.
  3. If the node to be splayed is two or more levels from the root, a zig-zig or zig-zag operation is performed on the result of splaying the subtree starting at that node, and the resulting tree is returned.

This is valid according to my teacher. However, the Wikipedia description of a splay tree says the zig step "will be done only as the last step in a splay operation" whereas in my algorithm it is the first step in a splay operation.

I want to implement a splay tree that performs the zig operation last instead of first, but I'm not sure how it would best be done. It seems to me that such an algorithm would become more complex, seeing as how one needs to find the node to be splayed before it can be determined whether a zig operation should be performed or not.

How can I implement this in Haskell (or some other functional language)?

Example

In this example we search for the value 4, prompting us to splay it to the top of the tree.

My algorithm (zig as the first step)

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

Wikipedia algorithm (zig as the last step)

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

Both trees are valid, but they have different structures. I want to implement the second one in a functional language, preferably Haskell.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

旧伤慢歌 2024-09-08 16:46:21

关键是构建一条到要展开的值的路径,然后从底部重建树,如果可能的话一次两层(以便可以做出 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 中的可运行单元测试和快速检查

The key is to build a path to the value to be splayed, then rebuild the tree from the bottom, two levels at a time if possible (so that the zig-zip vs. zig-zag determination can be made):

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)

You can find this code, along with runnable unit tests and quick checks in my repo.

×纯※雪 2024-09-08 16:46:21

您确定您正确阅读了维基百科的描述吗?步法有“zig”、“zig-zig”和“zig-zag”三种。 “zig”步骤定义为仅当x 是根的子级时才会发生。尽管有这些名称,但“zig-zig”和“zig-zag”步骤没有“zig”步骤作为第一个组件。

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

Are you sure you're reading the Wikipedia description correctly? There are three kinds of steps: "zig", "zig-zig", and "zig-zag". The "zig" step is defined to be something that happens only when x is a child of the root. Despite the names, the "zig-zig" and "zig-zag" steps don't have "zig" steps as a first component.

It sounds to me like your implementation follows the Wikipedia description in this respect.

岁吢 2024-09-08 16:46:21

你可以参考这门课程,讲得非常好用 OCaml 中的代码注释 Splay 树。

You can ref this course, which has a very good lecture note with code in OCaml for Splay tree.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文