通过一些练习来磨练我的二叉树技能,我决定实现一个splay树,如Wikipedia: Splay tree中所述。
我没有得到的一件事是关于插入的部分。
它说:
首先,我们在splay树中搜索x。如果x尚不存在,那么我们将找不到它,而是它的父节点y。其次,我们对y执行一个splay操作,它将y移动到splay树的根。第三,我们以适当的方式将新节点x作为root插入。这样,y是新根x的左或右子。
我的问题是:与文章中的其他例子相比,上述文字似乎过于简洁,为什么会这样?似乎这里遗漏了一些问题。例如,在将y节点向上扩展到根之后,我不能盲目地用x替换root,并将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
在我看来,首先进行splaying,然后以root身份正确添加新值的唯一方法意味着我必须检查以下条件(用于将splayed节点添加为新根的左子节点) :
但是,如果我要拆分我展示的节点,通过选择正确的子节点并将其作为新节点的右子节点附加,我会得到:
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
或者这种情况永远不会发生?
我的第二个问题是:如下所示重写操作不会轻松得多:
首先,我们在splay树中搜索x。如果x尚不存在,那么我们将找不到它,而是它的父节点y。 然后我们将新节点添加为父节点的左子节点。。第三,我们在我们添加的节点上执行展开操作,这将移动新值到了展开树的根。
强调我的表现我改变了什么。
答案 0 :(得分:5)
我不明白你描述的问题是怎么发生的。如果要在此树中插入13,首先必须找到它的位置:
10
/ \
5 15
/ \
11 20
从10开始,你从右边开始,从左边的15开始,从右边开始,然后你就没有了。如果13已经在树中,我们会发现它是11的右子。所以根据规则我们在11上执行一个splay操作,它将11移动到splay树的根:
11
/ \
10 15
/ \
5 20
然后我们添加13作为新的根,其中11作为左子:
13
/ \
11 15
/ \
10 20
/
5
现在没有问题。
首先,我们在splay树中搜索x。如果x尚不存在,那么我们将找不到它,而是它的父节点y。然后我们将新节点添加为父节点的左子节点或右子节点。第三,我们在添加的节点上执行splay操作,将新值移动到splay树的根。
这听起来对我来说也会起作用,但如果我是你,我只会尝试实现维基百科中描述的版本,因为很多人已经测试过,而且已经有很好的文档记录。
答案 1 :(得分:1)
“Splay Tree”立刻让我记得我刚读过CUJ的一篇文章,你可能会在那里找到一些见解:Implementing Splay Tree in C++。
第三,我们以适当的方式将新节点x作为root插入。这样,y是新根x的左或右子。
是的,但是这个新根x必须有2个孩子,这就是为什么这句话听起来有点混乱。
答案 2 :(得分:1)
新节点将像普通二进制搜索树一样添加到树中。然后,新节点将显示为根或根来自第一级。此外,当我们插入一个新节点时,我们需要找到放置它的位置,因此我们进行查找。并且所有操作(包括在splay树上查找)都会触发splay操作。也许这就是为什么维基百科的文章就是这样描述的。我只是插入新节点并将其展开。无论哪种方式,树都变得比它更平衡。工作得很好here