【问题标题】:Python3, get random node from Binary TreePython3,从二叉树中获取随机节点
【发布时间】:2017-04-30 00:11:05
【问题描述】:

我构建了一个二叉搜索树:

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 递归函数时,出于某种原因,我只能打印(使查找随机节点在逻辑上起作用)。但这真的不是我的主要目的,我的主要目的是返回“随机节点” 任何人都可以帮我修改getRandom函数,让“返回节点”真正起作用吗?

谢谢

【问题讨论】:

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


    【解决方案1】:

    我会将所有节点放在一个列表中,然后在 [0; length - 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)
    

    当然,您可以使用返回树大小的函数,但效率较低。

    它是如何工作的?

    如果没有当前节点,我们越界 => 返回无。

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

    如果我们想要的nthNode等于leftSize + 1,说明我们已经找到了想要的节点,所以我们返回它。

    如果我们想要的nthNode小于leftSize + 1,则表示nthNode在左孩子里面。我们不必更改 nthNode,因为右孩子的节点在 nthNode 之后。

    如果我们想要的nthNode大于leftSize + 1,则表示nthNode在右孩子里面。但是我们必须删除leftSize + 1,因为我们从左边删除了所有节点,这会改变第n个节点之前的节点数。

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

    编辑:我完全忘记了,这个算法并没有给你带有前序遍历的第 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,因为第一个元素是前序遍历中的根。

    关于精灵:

    • 你做了 nth - 1,因为你在前序遍历中通过了根

    • 你用

    【讨论】:

    • 嗨,Alex,您的解决方案是算法之一,我想使用不同的方法 :) 例如通过前序遍历读取树并通过 RandomNumber(th) 停止
    • 这是最好的算法之一,无需遍历所有树即可找到树中的第 n 个节点
    • 谢谢Alex,我明白你的逻辑了;实际上'nth'并不是真正的第n个节点的数量,它真的很像:给你一个随机数,从输入树中返回唯一(特定)节点。可用的 randomNumber 将从 [1] 到 [self.storage]。不同二叉树的相同随机数可能会给你不同的节点(不同的位置)。无论如何,我明白了,非常有趣的算法:)
    • 好吧,考虑到第二个 nth(预购),如果你有 'a' 没有左孩子并且有 'b' 作为右孩子,nth(2) 会给你右孩子,但是如果你有 'a' 和 'b' 作为左孩子而没有右孩子,nth(2) 会给你左孩子
    • 我今天下午又在想这个问题,根据你的想法(添加更多属性)。我想出了一个快速而肮脏的解决方案来获取随机节点而不处理大小和调整大小(插入和删除),请参阅我的最后一个 cmets
    【解决方案2】:

    节点类需要更改:

    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)
    

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

    【讨论】:

      【解决方案3】:

      在 BinaryTree 类中,我们添加了这些:

      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
      

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

      【讨论】:

        猜你喜欢
        • 2018-11-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多