展开树插入

发布于 2024-08-16 21:04:50 字数 2635 浏览 2 评论 0原文

通过一些练习来磨练我的二叉树技能,我决定实现一个展开树,如维基百科中所述:展开树

我没有得到的一件事是关于插入的部分。

它说:

首先,我们在展开树中搜索 x。如果x不存在,那么我们不会找到它,而是找到它的父节点y。其次,我们对 y 执行展开操作,这会将 y 移动到展开树的根。第三,我们以适当的方式插入新节点 x 作为根。这样,y 要么是新根 x 的左孩子,要么是右孩子。

我的问题是:与文章中的其他示例相比,上面的文字似乎过于简洁,这是为什么?这里似乎遗漏了一些问题。例如,在将 y 节点展开到根节点后,我不能盲目地将根节点替换为 x,并将 y 附加到 x 上作为左子节点或右子节点。

我们假设该值尚不存在于树中。

我有这棵树:

           10
          /  \
         5    15
        / \    \
       1   6    20

并且我想插入 8。根据上面的描述,我将查找 6 节点,在普通二叉树中,8 将作为 6 节点的右子节点添加,但是这里我首先必须将 6 个节点展开到 root:

            6
           / \
          5   10
         /     \
        1       15
                 \
                  20

那么这两个节点中的任何一个显然都是错误的:

          8                                  8
           \                                /
            6                              6
           / \                            / \
          5   10                         5   10
         /     \                        /     \
        1       15                     1       15
                 \                              \
                  20                             20

    6 is not greater than 8          10 is not less than 8

在我看来,首先进行展开,然后正确地将新值添加为 root 的唯一方法意味着我必须检查以下标准(用于将展开的节点添加为新根的左子节点):

  1. 我展开到根的节点小于新根 (6 < 8)
  2. 我展开到根的节点的最右子节点是也小于新的根 (20 8)

但是,如果我要分割我展开的节点,通过获取右子节点并将其附加为新节点的右子节点,我会得到以下结果:

                        8
                       / \
                      6   10
                     /     \
                    5       15
                   /         \
                  1           20

但是,这很简单吗改变总是会给我一棵正确的树?我很难举出一个例子,但这是否会导致以下结果:

  • 我想要添加的新值高于临时根(我展开到根的节点),但也高于最左边临时根的右孩子的孩子?

IE。一棵在展开后但在更换根部之前基本上看起来像这样的树?

                        10
                       /  \
                      5    15
                          /  \
                        11    20

我想添加 13,这将使新树像这样:

                        13
                       /  \
                     10    15
                     /    /  \
                    5   11    20  <-- 11, on the wrong side of 13

或者这永远不会发生?

我的第二个问题是:将操作重写如下不是更容易吗:

首先,我们在展开树中搜索 x。如果x不存在,那么我们不会找到它,而是找到它的父节点y。 然后我们将新节点添加为父节点的左子节点或右子节点。第三,我们对添加的节点执行展开操作,这将移动新值到展开树的根。

强调我的内容以显示我所做的更改。

Going through some excercises to hone my binary tree skills, I decided to implement a splay tree, as outlined in Wikipedia: Splay tree.

One thing I'm not getting is the part about insertion.

It says:

First, we search x in the splay tree. If x does not already exist, then we will not find it, but its parent node y. Second, we perform a splay operation on y which will move y to the root of the splay tree. Third, we insert the new node x as root in an appropriate way. In this way either y is left or right child of the new root x.

My question is this: The above text seems overly terse compared to the other examples in the article, why is that? It seems there are some gotchas left out here. For instance, after splaying the y node up to the root, I can't just blindly replace root with x, and tack y onto x as either left or right child.

Let's assume the value does not already exist in the tree.

I have this tree:

           10
          /  \
         5    15
        / \    \
       1   6    20

and I want to insert 8. With the description above, I will up finding the 6-node, and in a normal binary tree, 8 would be added as a right child of the 6-node, however here I first have to splay the 6-node up to root:

            6
           / \
          5   10
         /     \
        1       15
                 \
                  20

then either of these two are patently wrong:

          8                                  8
           \                                /
            6                              6
           / \                            / \
          5   10                         5   10
         /     \                        /     \
        1       15                     1       15
                 \                              \
                  20                             20

    6 is not greater than 8          10 is not less than 8

it seems to me that the only way to do the splaying first, and then correctly adding the new value as root would mean I have to check the following criteria (for adding the splayed node as the left child of the new root):

  1. the node I splayed to the root is less than the new root (6 < 8)
  2. the rightmost child of the node I splayed to the root is also less than the new root (20 8)

However, if I were to split up the node I splayed, by taking the right child and appending it as the right child of the new node, I would get this:

                        8
                       / \
                      6   10
                     /     \
                    5       15
                   /         \
                  1           20

But, is this simple alteration always going to give me a correct tree? I'm having a hard time coming up with an example, but could this lead to the following:

  • The new value I want to add is higher than the temporary root (the node I splayed to the root), but also higher than the leftmost child of the right-child of the temporary root?

Ie. a tree that would basically look like this after splaying, but before I replace the root?

                        10
                       /  \
                      5    15
                          /  \
                        11    20

and I want to add 13, which would make the new tree like this:

                        13
                       /  \
                     10    15
                     /    /  \
                    5   11    20  <-- 11, on the wrong side of 13

or can this never happen?

My second question is this: Wouldn't it be much easier to just rewrite the operation as follows:

First, we search x in the splay tree. If x does not already exist, then we will not find it, but its parent node y. Then we add the new node as either a left or right child of the parent node. Thirdly, we perform a splay operation on the node we added which will move the new value to the root of the splay tree.

emphasis mine to show what I changed.

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

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

发布评论

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

评论(3

弱骨蛰伏 2024-08-23 21:04:50

我不明白你所描述的问题是如何发生的。如果你想将 13 插入到这棵树中,你首先必须找到它所在的位置:

                    10
                   /  \
                  5    15
                      /  \
                    11    20

从 10 开始向右,从 15 向左,从 11 向右……然后你就没有更多的元素了。如果 13 已经在树中,我们就会发现它是 11 的右子节点。因此,根据规则,我们对 11 执行展开操作,这会将 11 移动到展开树的根:

                    11
                   /  \
                  10   15
                 /       \
                5         20

然后我们添加 13 作为新根,左孩子为 11:

                    13
                   /  \
                  11   15
                 /       \
                10        20
               /
              5

现在没问题了。

首先,我们在展开树中搜索 x。如果x不存在,那么我们不会找到它,而是找到它的父节点y。然后我们将新节点添加为父节点的左子节点或右子节点。第三,我们对添加的节点执行展开操作,这会将新值移动到展开树的根。

在我看来,这也可以工作,但如果我是你,我只会尝试实现维基百科中描述的版本,因为很多人已经测试过它并且它已经有很好的记录。

I don't see how the problem you describe could happen. If you want to insert 13 into this tree you first have to find where it would be:

                    10
                   /  \
                  5    15
                      /  \
                    11    20

From 10 you go right, from 15 you go left, from 11 you go right... and then you have no more elements. If 13 had been in the tree, we would have found it as a right child of 11. So according to the rule we perform a splay operation on 11 which will move 11 to the root of the splay tree:

                    11
                   /  \
                  10   15
                 /       \
                5         20

Then we add 13 as the new root, with 11 as the left child:

                    13
                   /  \
                  11   15
                 /       \
                10        20
               /
              5

Now there is no problem.

First, we search x in the splay tree. If x does not already exist, then we will not find it, but its parent node y. Then we add the new node as either a left or right child of the parent node. Thirdly, we perform a splay operation on the node we added which will move the new value to the root of the splay tree.

This sounds to me like it would work too, but if I were you, I'd just try to implement the version as it described in Wikipedia since lots of people have tested that and it is already well documented.

ら栖息 2024-08-23 21:04:50

“Splay Tree”立刻让我想起了不久前在CUJ读到的一篇文章,你可能会在那里找到一些见解:用 C++ 实现展开树

第三,我们以适当的方式插入新节点 x 作为根。这样,y 要么是新根 x 的左孩子,要么是右孩子。

是的,但是这个新的根 x 必须有 2 个孩子,这就是为什么这句话可能听起来令人困惑。

"Splay Tree" immediately made me remember an article in CUJ I read a while ago, you might find some insight there: Implementing Splay Tree in C++.

Third, we insert the new node x as root in an appropriate way. In this way either y is left or right child of the new root x.

Yes, but this new root x has to have 2 children, that's why this sentence might sound confusing.

绝情姑娘 2024-08-23 21:04:50

新节点将像普通的二叉搜索树一样添加到树中。然后新节点将向上展开为根节点或根节点的第一层。另外,当我们插入一个新节点时,我们需要找到放置它的位置,所以我们进行查找。并且包括在展开树上查找在内的所有操作都会触发展开操作。可能这就是维基百科文章如此描述的原因。我只是插入新节点并将其展开。无论哪种方式,树都会变得比以前更加平衡。 这里工作得很好

the new node would be added to the tree just like a normal binary search tree. Then the new node would be splayed up to be the root or the first level from the root. Also, when we insert a new node, we need to find the location to put it, so we do a find. And all operations including find on a splay tree trigger a splay operation. May be thats why the wikipedia article describes it like that. I just insert the new node and splay it up. Either way the tree becomes better balanced than it was. works just fine here

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