【问题标题】:Bug in Splay tree insertion implementationSplay 树插入实现中的错误
【发布时间】:2017-09-24 19:35:53
【问题描述】:

我一直在尝试实现splay树,但到目前为止没有成功。之前我成功实现了二叉搜索树和avl树,由于splay树是二叉搜索树的变体,所以插入代码和旋转代码很好。我面临的唯一问题是每次插入节点时将节点向上移动到根。这是我的代码

class SplayTree:

    def __init__(self):
        self.root = None
        self.size = 0

    def moveUp(self, currentNode):
        if currentNode.parent:
            if currentNode.parent.parent is not None:
                #zig zag
                if currentNode.isRightChild() and currentNode.parent.isLeftChild():
                    self.rotateLeft(currentNode.parent)
                    self.rotateRight(currentNode.parent)

                elif currentNode.isLeftChild() and currentNode.parent.isRightChild():
                    self.rotateRight(currentNode.parent)
                    self.rotateLeft(currentNode.parent)

                #zig zig
                if currentNode.isLeftChild() and currentNode.parent.isLeftChild():
                    self.rotateRight(currentNode.parent.parent)
                    self.rotateRight(currentNode.parent)

                elif currentNode.isRightChild() and currentNode.parent.isRightChild():
                    self.rotateLeft(currentNode.parent.parent)
                    self.rotateLeft(currentNode.parent)
                self.moveUp(currentNode)

            #zig
            if currentNode.isLeftChild():
                self.rotateRight(currentNode.parent)
            elif currentNode.isRightChild():
                self.rotateLeft(currentNode.parent)
            self.moveUp(currentNode)

        else:
            return

    def put(self,key,val):
        if self.root:
            self._put(key,val,self.root)
        else:
            self.root = TreeNode(key,val)
        self.size += 1

    def _put(self,key,val,currentNode):               
         if key < currentNode.key:
            if currentNode.hasLeftChild():
                self._put(key,val,currentNode.leftChild)
            else:
                currentNode.leftChild = TreeNode(key,val,parent=currentNode)
            self.moveUp(currentNode.leftChild)

         else:
            if currentNode.hasRightChild():
                self._put(key,val,currentNode.rightChild)
            else:
                currentNode.rightChild = TreeNode(key,val,parent=currentNode)
            self.moveUp(currentNode.rightChild)

    def __setitem__(self, key, value):
        self.put(key,value)

    def rotateLeft(self, rotRoot):
        newRoot = rotRoot.rightChild
        if newRoot.leftChild is not None:
            rotRoot.rightChild = newRoot.leftChild
            newRoot.leftChild.parent = rotRoot
        # if subtree is at top or somewhere in between
        # make connection between node and parent
        newRoot.parent = rotRoot.parent
        if rotRoot.parent is None:
            self.root = newRoot
        # make connection between parent and node
        else:
            if rotRoot.isLeftChild():
                rotRoot.parent.leftChild = newRoot
            else:
                rotRoot.parent.rightChild = newRoot
        newRoot.leftChild = rotRoot
        rotRoot.parent = newRoot

    def rotateRight(self, rotRoot):
        newRoot = rotRoot.leftChild
        if newRoot.rightChild is not None:
            rotRoot.leftChild = newRoot.rightChild
            newRoot.rightChild.parent = rotRoot
        newRoot.parent = rotRoot.parent
        if rotRoot.parent is None:
            self.root = newRoot
        else:
            if rotRoot.isLeftChild():
                rotRoot.parent.leftChild = newRoot
            else:
                rotRoot.parent.rightChild = newRoot
        newRoot.rightChild = rotRoot
        rotRoot.parent = newRoot

    def inorder(self):
        print("INORDER")
        if self.root:
            self._inorder(self.root)
            print()
        else:
            return None

    def _inorder(self,currentNode):
        if currentNode:
            self._inorder(currentNode.leftChild)
            print(currentNode.key,end=" ")
            self._inorder(currentNode.rightChild)

class TreeNode:

    def __init__(self,key,val,left = None,right = None,parent = None):
        self.key = key
        self.payload = val
        self.leftChild = left
        self.rightChild = right
        self.parent = parent

    def hasLeftChild(self):
        return self.leftChild

    def hasRightChild(self):
        return self.rightChild

    def isLeftChild(self):
        return self.parent and self.parent.leftChild == self

    def isRightChild(self):
        return self.parent and self.parent.rightChild == self

    def isLeaf(self):
        return not (self.leftChild or self.rightChild)

    def hasAnyChildren(self):
        return self.leftChild or self.rightChild

    def hasBothChildren(self):
        return self.leftChild and self.rightChild

st = SplayTree()
st[32] = "Cat"
st[55] = "Dog"
st[10] = "Lion"
st[41] = "Zebra"
st[19] = "Fox"
st[1] = "Wolf"
st[16] = "Tiger"
st[12] = "Pig"
st.inorder()

我认为我的 moveUp() 方法出了问题。但我似乎无法弄清楚到底出了什么问题?

【问题讨论】:

  • 我认为您的轮换方法有些错误。 rotateLeft 中的第一个 if 中的行 rotRoot.rightChild = newRoot.leftChild(以及 rotateRight 中的等效行)应该无条件运行。否则,您可以有一个从 rotRoot 到应该被 None 替换的 newRoot 的剩余连接。
  • @Blckknght 这是一个很好的观察。现在代码正在运行,但给出的结果不正确。添加的最后一个节点不会进入根目录!我重新检查了 moveUp() 代码,似乎没有找到任何可疑的东西。
  • @Blckknght 哦,现在它工作正常......只是做了一些细微的调整。再次感谢!

标签: python-3.x data-structures tree splay-tree


【解决方案1】:

您的代码中有两个问题。

首先是您的轮换方法中有一个微妙的错误,您有时未能将其中一个子链接设置为NonerotateLeft 中的第一个 if 中的行 rotRoot.rightChild = newRoot.leftChild(以及 rotateRight 中的等效行)应该无条件运行。只需将其移出if 即可修复它。

第二个问题是你打电话给moveUp 太频繁了。您在每次递归调用_put 时运行它,但由于它moveUp 也递归调用自身,这意味着它运行得太频繁了。缩进_put 中的调用,使它们成为您正在创建新节点的else 块的一部分。这样一来,您将只从最后一个 _put 调用中调用 moveUp,而不是从所有它们中调用。

def _put(self,key,val,currentNode):               
     if key < currentNode.key:
        if currentNode.hasLeftChild():
            self._put(key,val,currentNode.leftChild)
        else:
            currentNode.leftChild = TreeNode(key,val,parent=currentNode)
            self.moveUp(currentNode.leftChild)                     # increase indent here!

     else:
        if currentNode.hasRightChild():
            self._put(key,val,currentNode.rightChild)
        else:
            currentNode.rightChild = TreeNode(key,val,parent=currentNode)
            self.moveUp(currentNode.rightChild)                    # here too

def rotateLeft(self, rotRoot):
    newRoot = rotRoot.rightChild
    rotRoot.rightChild = newRoot.leftChild       # move this line up, out of the if
    if newRoot.leftChild is not None:
        newRoot.leftChild.parent = rotRoot
    newRoot.parent = rotRoot.parent
    if rotRoot.parent is None:
        self.root = newRoot
    # make connection between parent and node
    else:
        if rotRoot.isLeftChild():
            rotRoot.parent.leftChild = newRoot
        else:
            rotRoot.parent.rightChild = newRoot
    newRoot.leftChild = rotRoot
    rotRoot.parent = newRoot

def rotateRight(self, rotRoot):
    newRoot = rotRoot.leftChild
    rotRoot.leftChild = newRoot.rightChild       # this one as well
    if newRoot.rightChild is not None:
        newRoot.rightChild.parent = rotRoot
    newRoot.parent = rotRoot.parent
    if rotRoot.parent is None:
        self.root = newRoot
    else:
        if rotRoot.isLeftChild():
            rotRoot.parent.leftChild = newRoot
        else:
            rotRoot.parent.rightChild = newRoot
    newRoot.rightChild = rotRoot
    rotRoot.parent = newRoot

【讨论】:

  • 是的,正如你所说,特别是无条件调用 rotRoot.rightChild = newRoot.leftChild 和另一个相同的调用,这似乎完全解决了问题。谢谢!
【解决方案2】:

尝试在两个地方改变你的 moveUp:

if currentNode.isRightChild() and currentNode.parent.isLeftChild():
    self.rotateLeft(currentNode.parent)
    self.rotateRight(currentNode.parent.parent) // Here
elif currentNode.isLeftChild() and currentNode.parent.isRightChild():
    self.rotateRight(currentNode.parent)
    self.rotateLeft(currentNode.parent.parent) // Here

应该有帮助

【讨论】:

  • 这是不正确的。经过一轮轮换后,currentNode 在树中替换了它原来的parent,所以原来的祖父母现在是直接父母。问题中的代码是正确的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-11-19
  • 1970-01-01
  • 2012-11-05
  • 1970-01-01
  • 2022-11-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多