创建二叉树

时间:2020-05-31 21:00:52

标签: python class if-statement binary-tree self

我搜索的有关二叉树的大多数问题都显示了二叉搜索树的实现,而不是二叉树。完整的二叉树的术语是:

  • 一棵空树或它有1个节点和2个子节点,每个子节点 孩子是另一棵二叉树。
  • 所有级别都已满(可能是最后一个级别除外)
  • 最底层的所有叶子都是 尽可能左。

我想出了一个概念,但是似乎无法正确执行递归操作?有人知道我在做什么错吗?

function clearbox(){
    if (x == myInput.value){
        var x = myArray[Math.floor(Math.random()*myArray.length)]);
        document.getElementById('myInput').value = ''
        document.getElementById("word").innerHTML = x
    } else {
        document.getElementById('myInput').value = ''   ;
    }

3 个答案:

答案 0 :(得分:3)

您的代码中的问题是您多次添加相同的值。添加该节点,然后再递归进行更深的操作。

更深层次的问题是,您真的不知道在到达树的最低层之前,在之前插入节点的位置,并且检测到该层的位置不完整。找到正确的插入点可能需要遍历整棵树...这正在破坏您最初希望使用二叉树获得的速度增益。

我在这里提供三种解决方案,从最有效的开始:

1。使用列表作为树实现

对于完整的树,需要特别注意:如果按级别对节点进行编号(从0开始为根,在从左到右的每个级别内),您会注意到节点的父级编号为(k-1)/ 2 时,其自身编号为 k 。在另一个方向上:如果编号为 k 的节点有子节点,则其左子节点的编号为 k * 2 + 1 ,而右子节点的编号为1更大。

因为树是完整的,所以此编号永远不会有间隔,因此您可以将节点存储在列表中,并将该列表的索引用于节点编号。现在,将节点添加到树仅意味着您将其追加到该列表。您只有树列表,而不是Node对象,并且该列表中的索引是您的节点引用。

这是一个实现:

class CompleteTree(list):
    def add(self, key):
        self.append(key)
        return len(self) - 1

    def left(self, i):
        return i * 2 + 1 if i * 2 + 1 < len(self) else -1

    def right(self, i):
        return i * 2 + 2 if i * 2 + 2 < len(self) else -1            

    @staticmethod
    def parent(i):
        return (i - 1) // 2

    def swapwithparent(self, i):
        if i > 0:
            p = self.parent(i)
            self[p], self[i] = self[i], self[p]

    def inorder(self, i=0):
        left = self.left(i)
        right = self.right(i)
        if left >= 0:
            yield from self.inorder(left)
        yield i
        if right >= 0:
            yield from self.inorder(right)

    @staticmethod
    def depth(i):
        return (i + 1).bit_length() - 1

这里是一个演示,它创建您的示例树,然后按遍历的顺序打印访问的键,并以它们在树中的深度为准:

tree = CompleteTree()
tree.add(1)
tree.add(2)
tree.add(3)
tree.add(4)
tree.add(5)
for node in tree.inorder():
    print("  " * tree.depth(node), tree[node])

当然,这意味着您必须引用与使用实型Node类时有所不同的节点,但是效率提高是有回报的。

2。使用额外的属性

如果您知道(子)树中有多少个节点,那么从该数字的位表示形式,您可以确切知道应该在哪里添加下一个节点。

例如,在您的示例树中,您有5个节点。假设您要向该树添加6。根节点会告诉您当前有5个,因此您需要将其更新为6。在二进制数110中。忽略最左边的1位,其余位将告诉您是左移还是右移。在这种情况下,您应该向右(1),最后向左(0),在该方向上创建节点。您可以迭代或递归执行此操作。

这是递归的实现:

class Node():
    def __init__(self, key):
        self.key = key
        self.left = None
        self.right = None
        self.count = 1

    def add(self, key):
        self.count += 1
        if self.left is None:
            self.left = Node(key)
        elif self.right is None:
            self.right = Node(key)
        # extract from the count the second-most significant bit:
        elif self.count & (1 << (self.count.bit_length() - 2)):
            self.right.add(key)
        else:
            self.left.add(key)

    def inorder(self):
        if self.left:
            yield from self.left.inorder()
        yield self
        if self.right:
            yield from self.right.inorder()

tree = Node(1)
tree.add(2)
tree.add(3)
tree.add(4)
tree.add(5)
for node in tree.inorder():
    print(node.key)

3。没有多余的财产

如果没有属性可以添加到Node对象中,则需要进行更广泛的搜索以找到正确的插入点:

class Node():
    def __init__(self, key):
        self.key = key
        self.left = None
        self.right = None

    def newparent(self):
        # Finds the node that should serve as parent for a new node
        # It returns a tuple:
        #   if parent found: [-1, parent for new node]
        #   if not found: [height, left-most leaf]
        # In the latter case, the subtree is perfect, and its left-most  
        # leaf is the node to be used, unless self is a right child 
        # and its sibling has the insertion point.
        if self.right:
            right = self.right.newparent()
            if right[0] == -1: # found inbalance
                return right
            left = self.left.newparent()
            if left[0] == -1: # found inbalance
                return left
            if left[0] != right[0]:
                return [-1, right[1]] # found inbalance
            # temporary result in perfect subtree
            return [left[0]+1, left[1]]
        elif self.left:
            return [-1, self] # found inbalance
        # temporary result for leaf
        return [0, self]

    def add(self, key):
        _, parent = self.newparent()
        if not parent.left:
            parent.left = Node(key)
        else:
            parent.right = Node(key)

    def __repr__(self):
        s = ""
        if self.left:
            s += str(self.left).replace("\n", "\n  ")
        s += "\n" + str(self.key)
        if self.right:
            s += str(self.right).replace("\n", "\n  ")
        return s

tree = Node(1)
tree.add(2)
tree.add(3)
tree.add(4)
tree.add(5)
print(tree)

这从右到左递归搜索树,以找到要添加的节点的候选父级。

对于大树,可以通过基于根路径到叶路径的长度在二进制路径中进行二进制搜索来进行一些改进。但是它仍然不如前两个解决方案有效。

答案 1 :(得分:0)

您可以使用sklearn决策树,因为它们也可以设置为二进制决策树。链接到文档here

答案 2 :(得分:0)

您确实需要以某种方式扩充树。由于这不是二叉搜索树,因此关于每个节点的唯一真实信息是它是否具有左右子节点。不幸的是,这对导航完整的二叉树没有帮助。想象一个具有10个级别的完整二叉树。在第9层之前,每个节点都有一个左孩子和一个右孩子,因此您无法知道该走哪条路。所以问题是,您要向每个节点添加哪些信息?我要添加那棵树中的节点数。

维护计数很容易,因为每次您下降一个子树时,您就会知道在该节点的计数上添加一个。您要识别的是最左边的不完美子树。每个完美的二叉树的n = 2 ^ k-1,其中k是级别数,n是节点数。有一种快速简便的方法可以检查数字是否小于1的2的幂(请参阅this question的第一个答案),实际上,在完整的二叉树中,每个节点最多有一个子代,即“完美二叉树的根。遵循简单的规则添加节点:

  1. 如果左孩子为None,则设置root.left = Node(key)并返回
  2. 否则,如果正确的孩子是None,则设置root.right = Node(key)并返回
  3. 如果当前节点的子节点之一是不完善子树的根,请将该节点设为当前节点(从该子树向下下降)​​
  4. 如果大小不相等,则将具有较小子树的节点作为当前节点。
  5. 否则,将左孩子作为当前节点。

通过以植根于此的子树的大小来扩展每个节点,您在每个节点上都拥有了构建递归解决方案所需的所有信息。