Python3,从二叉树中获取随机节点

时间:2017-04-30 00:11:06

标签: python-3.x random binary-tree

我构建了二进制搜索树:

import random

class Node(object):
 def __init__(self, key):
    self.key = key
    self.leftChild = None
    self.rightChild = None
    self.parent = None

 def hasLeftChild(self):
    if self.leftChild:
        return True
    else:
        return False

 def hasRightChild(self):
    if self.rightChild:
        return True
    else:
        return False

 def isLeftChild(self):
    if self.parent.leftChild == self:
        return True
    else:
        return False

 def isRightChild(self):
    if self.parent.rightChild == self:
        return True
    else:
        return False

 def isRoot(self):
    if not self.parent:
        return True
    else:
        return False


class BinaryTree(object):
 def __init__(self):
    self.root = None
    self.storage = 0

 def addNode(self, newItem):
    if self.storage == 0:
        self.root = Node(newItem)
    else:
        currentNode = self.root
        self.__addNode(currentNode, newItem)
    self.storage += 1

 def __addNode(self, currentNode, newItem):
    if newItem < currentNode.key:
        if currentNode.hasLeftChild():
            self.__addNode(currentNode.leftChild, newItem)

        else:
            currentNode.leftChild = Node(newItem)
            currentNode.leftChild.parent = currentNode
    if newItem > currentNode.key:
        if currentNode.hasRightChild():
            self.__addNode(currentNode.rightChild, newItem)
        else:
            currentNode.rightChild = Node(newItem)
            currentNode.rightChild.parent = self

 def findNode(self, searchKey):
    if self.storage == 0:
        return None
    else:
        if self.root.key == searchKey:
            return self.root
        else:
            currentNode = self.root
            return self.__findNode(currentNode, searchKey)

 def __findNode(self, currentNode, searchKey):
    if searchKey == currentNode.key:
        return currentNode
    if searchKey < currentNode.key:
        if currentNode.hasLeftChild():
            return self.__findNode(currentNode.leftChild, searchKey)
    if searchKey > currentNode.key:
        if currentNode.hasRightChild():
            return self.__findNode(currentNode.rightCHild, searchKey)
    return None

 def getRandomNode(self):
    randomNum = random.randrange(self.storage)
    currentNode = self.root
    self.__getRandomNode(currentNode, randomNum)

 def __getRandomNode(self, currentNode, numCount):
    if numCount == 0:
        print('Find node {0}'.format(currentNode.key))
        return currentNode, numCount
    else:
        if currentNode.hasLeftChild():
            numCount -= 1
            node, numCount = self.__getRandomNode(currentNode.leftChild, numCount)

        if currentNode.hasRightChild():
            numCount -= 1
            node, numCount = self.__getRandomNode(currentNode.rightChild, numCount)
        return None, numCount

print('-----Add Node to BTree-----')
myBinaryTree = BinaryTree() 
myBinaryTree.addNode(15)
myBinaryTree.addNode(10)
myBinaryTree.addNode(20)
myBinaryTree.addNode(9)
myBinaryTree.addNode(13)
myBinaryTree.addNode(12)
print('-----Get Random Node-----')
myBinaryTree.getRandomNode()

当我构建getRandom递归函数时,由于某些原因,我只能打印(使查找随机节点在逻辑上起作用)。但这不是我的主要目的,我的主要目的是返回&#34;随机节点&#34; 任何人都可以帮我修改getRandom函数并让&#34;返回Node&#34;真的有用吗?

谢谢

3 个答案:

答案 0 :(得分:0)

我会将所有节点放在一个列表中,然后在[0;长度 - 1]并采取该节点

因此,另一种算法在每个节点上都需要一个size属性,该节点是以节点为根的子树的大小。

示例:
/ - 2
1
\ - 3 - 4

1的节点的大小是4,2的节点的大小是1,3的节点的大小是2,4的节点的大小是1

以下是算法:

def findNthNode(currentNode, nth):
    if currentNode == None:
        return None
    leftSize = 0
    if currentNode.hasLeftChild():
        leftSize = currentNode.leftChild.size
    if nth == leftSize + 1:
        return currentNode
    elif nth < leftSize + 1:
        return findNthNode(currentNode.leftChild, nth)
    else:
        return findNthNode(currentNode.rightChild, nth - leftSize - 1)

当然,你可以使用一个返回树大小的函数,但效率会低一些。

它是如何运作的?

如果没有当前节点,我们超出界限=&gt;返回无。

如果没有左子,则左子树的大小为0.

如果我们想要的nthNode等于leftSize + 1,则意味着我们找到了我们想要的节点,因此我们返回它。

如果我们想要的nthNode小于leftSize + 1,则意味着nthNode在left子节点内。我们不必更改nthNode,因为右子节点位于nthNode之后。

如果我们想要的nthNode大于leftSize + 1,则意味着nthNode在右子节点内。但是我们必须删除leftSize + 1,因为我们从左边删除了所有节点,这些节点改变了nthNode之前的节点数。

我希望你理解这个算法,因为它有点难以解释

编辑:我完全忘记了,这个算法没有给你带有preorder遍历的nthNode,但实际上是第n个最大的节点,希望它仍然适合你的需求

我已经研究过了,我刚刚找到了一个解决前序遍历的解决方案:

def findNthNode(currentNode, nth):
    if currentNode == None:
        return None
    leftSize = 0
    if currentNode.hasLeftChild():
        leftSize = currentNode.leftChild.size
    if nth == 1:
        return currentNode
    elif nth <= leftSize + 1:
        return findNthNode(currentNode.leftChild, nth - 1)
    else:
        return findNthNode(currentNode.rightChild, nth - leftSize - 1)

只有三处修改:

在if上,检查nth == 1,因为第一个元素是前序遍历中的根。

关于elif:

  • 你做了nth - 1,因为你经历了前序遍历中的根

  • 你做&lt; =而不是&lt;因为你用它计算根

答案 1 :(得分:0)

需要更改节点类:

class Node(object):
    def __init__(self, key):
        self.key = key
        self.leftChild = None
        self.rightChild = None
        self.parent = None
        self.size = 0

BinaryTree类中的addNode函数需要更改:

def addNode(self, newItem):
    if self.storage == 0:
        self.root = Node(newItem)
        self.root.size = 1
    else:
        currentNode = self.root
        self.__addNode(currentNode, newItem)
    self.storage += 1

def __addNode(self, currentNode, newItem):
    if newItem < currentNode.key:
        if currentNode.hasLeftChild():
            self.__addNode(currentNode.leftChild, newItem)

        else:
            currentNode.leftChild = Node(newItem)
            currentNode.leftChild.parent = currentNode
            currentNode.leftChild.size = 1
            self.updateNodeSize(currentNode.leftChild)
    if newItem > currentNode.key:
        if currentNode.hasRightChild():
            self.__addNode(currentNode.rightChild, newItem)
        else:
            currentNode.rightChild = Node(newItem)
            currentNode.rightChild.parent = currentNode
            currentNode.rightChild.size = 1
            self.updateNodeSize(currentNode.rightChild)

另外,每次插入后都需要添加新功能:

def updateNodeSize(self, currentNode):
    if not currentNode.isRoot():
        currentNode.parent.size += 1
        self.updateNodeSize(currentNode.parent)

如果你有BinaryTree Class的删除功能,你还需要在每次删除后为更新节点大小编写新函数。 为了从现有树中获取一个随机节点,增加插入和删除的时间复杂度,这可能是我们需要考虑的另一个问号:) 无论如何,相同问题没有“最佳代码”,但“更好的代码”。

答案 2 :(得分:0)

在BinaryTree Class中,我们添加以下内容:

class BinaryTree(object):
    def __init__(self):
        self.root = None
        self.storage = 0
        self.toatlCount = 0
        self.randomNode = None

对于添加randomNode函数,我们更改为:

def getRandomNode(self):
    randomNum = random.randrange(self.storage)
    currentNode = self.root
    # initial self.randomNode each time
    self.randomNode = None
    self.__getRandomNode(currentNode, randomNum)
    return self.randomNode

def __getRandomNode(self, currentNode, numCount):
    if self.toatlCount == numCount:
        self.randomNode = currentNode
    else:
        if currentNode.hasLeftChild():
            self.toatlCount += 1
            self.__getRandomNode(currentNode.leftChild, numCount)
        if currentNode.hasRightChild():
            self.toatlCount += 1
            self.__getRandomNode(currentNode.rightChild, numCount)
    return None

基于此解决方案,您将始终获得基于匹配条件的最后一个递归节点。