【问题标题】:BST with connection to parent - loop连接到父循环的 BST
【发布时间】:2019-11-29 22:08:26
【问题描述】:

我在 getMinimal() 方法中遇到无限循环问题。它以这种方式工作: 1)取节点, 2)如果节点左侧有其他节点 - 转到其他节点。 3)重复直到节点在左侧有某物 4)返回最小节点。

但有时它会在无限循环中工作,例如从 1000 到 400,然后到 4 再到 1000!我不知道我在哪里犯错。我多次查看此代码,每个指向父/左/右节点的“指针”都可以!请帮忙。 算法适用于“手写”树 - ~20nodes。我想在更好的情况下测试它 - 2500 个节点,由随机库生成(从 -10k 到 10k)。

import random


class Node:
    def __init__(self, val):
    self.val = val
    self.parent = None
    self.right = None
    self.left = None
    # Class of node.
def str(self):
    return str(self.val)


class MyTree:
        def __init__(self, node):
            self.root = node

        def insert(self, node):
            current = self.root
            a = True
            while a:
                if node.val > current.val:
                    if current.right is not None:
                        current = current.right
                        continue
                    else:
                        current.right = node
                        node.parent = current
                        a = False
                if node.val <= current.val:
                    if current.left is not None:
                        current = current.left
                        continue
                    else:
                        current.left = node
                        node.parent = current
                        a = False

        def search(self, node):
            current = self.root

            while node.val != current.val:
                if node.val > current.val:
                    current = current.right
                    continue
                elif node.val <= current.val:
                    current = current.left
                    continue
            if node.val == current.val:
                return current
            else:
                print("There is no such node!")

        def delete(self, node):

            if isinstance(node, (float, int)):
                node = self.search(node)

            if node is self.root:
                self.__deleteRoot()
                return
            else:
                if node.right is None and node.left is None:
                    self.__deleteNN(node)
                    return
                if node.right is None and node.left is not None:
                    self.__deleteLN(node)
                    return
                if node.right is not None and node.left is None:
                    self.__deleteNR(node)
                    return
                if node.right is not None and node.left is not None:
                    self.__deleteLR(node)
                    return

        def __deleteNN(self, node):
            if node.parent.left is node:
                node.parent.left = None
            if node.parent.right is node:
                node.parent.right = None

        def __deleteLN(self, node):
            parent = node.parent
            son = node.left
            # parent replaced
            if parent.left is node:
                parent.left = son
            if parent.right is node:
                parent.right = son
            son.parent = parent


        def __deleteNR(self,node):
            parent = node.parent
            son = node.right
            # replace parent
            if parent.left is node:
                parent.left = son
            if parent.right is node:
                parent.right = son
            son.parent = parent

        def __deleteLR(self, node):

            minimal = self.getMinimal(node.right)
            if minimal.parent.left is minimal:
                minimal.parent.left = None
            if minimal.parent.right is minimal:
                minimal.parent.right = None
            # parent of minimal done..
            if node.parent.left is node:
                node.parent.left = minimal
            if node.parent.right is node:
                node.parent.right = minimal
            minimal.right = node.right
            minimal.left = node.left

        def getMinimal(self, node):
            k = node
            while k.left is not None:
                k = k.left
            return k

        def getMax(self):
            current = self.root
            while current.right:
                current = current.right
            return current

        def __trav(self, node):
            if not node:
                return
            print(node.val)
            self.__trav(node.left)
            self.__trav(node.right)

        def printTrav(self):
            self.__trav(self.root)

        def __deleteRoot(self):
            if self.root.left is None and self.root.right is None:
                self.root = None
                return
            if self.root.left is None and self.root.right is not None:
                # left empty,right full
                self.root.right.parent = None
                self.root = self.root.right
                return
            if self.root.left is not None and self.root.right is None:
                # right empty, left full
                self.root.left.parent = None
                self.root = self.root.left
                return
            # node has both children
            if self.root.left is not None and self.root.right is not None:
                temp = self.getMinimal(self.root.right)  # minimal from right subtree
                # sometimes it could be like this..
                # r
                #   \
                #    x
                if temp.parent.left is temp:
                    temp.parent.left = None
                else:
                    temp.parent.right = None

                self.root.left.parent = temp
                self.root.right.parent = temp
                temp.right = self.root.right
                temp.left = self.root.left
                self.root = temp
                self.root.parent = None

                return

        def search(self, val):
            node = self.root
            if node.val == val:
                return node
            if val > node.val and node.right is not None:
                node = node.right
            if val < node.val and node.left is not None:
                node = node.left
            else:
                print("There's no such value!")
                return
        def printMax(self):
            print(self.getMax().val)
        def printMin(self):
            print(self.getMinimal(self.root).val)

arr=[None]*2500
for x in range(2500):
     arr[x]=Node(random.randint(-10000,10000))

myTree = MyTree(arr[0])
for x in range(1,2500):
     myTree.insert(arr[x])

for x in range(2500):
     myTree.delete(arr[x])

【问题讨论】:

  • getMinimal 方法的目的是什么?请编辑问题以解释您要解决的问题。如果我们不知道代码要做什么,就很难说代码出了什么问题。见stackoverflow.com/help/how-to-ask
  • 所以 - geeksforgeeks.org/binary-search-tree-set-2-delete 如果我想删除具有两个节点(左和右)的节点,我必须在右子树上找到一个最小节点。然后我必须删除我的节点,然后插入最小。我的问题是(我认为)代码中的错误(可能在删除中)。搜索“导致”无限循环。
  • 请修正你的代码缩进并修正重复的方法名search
  • @trincot - 它不是重复的 - 它已超载。一个搜索节点作为对象,另一个通过传递的值搜索第一个节点。
  • 这不是重载,而是(无用的)覆盖,因为 Python 不支持(例如)Java 中的重载。见here

标签: python algorithm binary-search-tree


【解决方案1】:

你定义search两次是可疑的。

话虽如此,这就是我将如何调试它。我会修改您的程序以从文件中读取,尝试运行,然后检测到无限循环并退出。现在写随机文件,直到你有一个导致你崩溃的文件。

一旦您有一个显示错误的随机文件,下一步就是使其最小化。这是一个可以让您做到这一点的安全带。

import itertools
flatten = itertools.chain.from_iterable

# bug_found should be a function that takes a list of elements and runs your test.
# example should be an array that demonstrates the bug.
def find_minimal (bug_found, example):
    parts = [example]
    while 1 < max(len(part) for part in parts):
        i = 0
        while i < len(parts):
            if 1 == len(parts[i]):
                i = i + 1
            else:
                part = parts.pop(i)
                # Divide in 2.
                mid = len(part)/2
                part1 = part[0:mid]
                part2 = part[mid:len(part)]

                # Do we need part1?
                parts.insert(i, part1)
                if bug_found(flatten(parts)):
                    i = i + 1
                    parts.insert(i, part2)
                else:
                    parts[i] = part2

                # Do we need part2?
                if bug_found_func(flatten(parts)):
                    i = i + 1
                else:
                    parts.pop(i)
    return list(flatten(parts))

让它运行,一段时间后它很可能会找到一个小例子。这将极大地帮助调试。

【讨论】:

  • 所以,无限循环正在搜索中,因为节点中的指针是错误的。例如(有时):节点 a 将其自身作为左侧。在其他情况下,它就像这样 a.left = b, b.left = c,c.left=a - 它会导致无限循环。但我不知道我的指针有什么问题 - 删除根还是删除节点?
  • @patrykoz 我的建议是关于如何从随机样本中生成最小样本。希望一个最小的例子足够小,可以手动运行,这样你就可以看到哪里发生了错误。
【解决方案2】:

所以 - 我在代码中发现了 2 个严重的错误。在 LR(“标准”节点和根)中。正如我所怀疑的 - 错误在指针中。现在树正在工作(针对 20k、30k 和 100k 节点测试了几次)。解决了。​​

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-01-20
    • 1970-01-01
    • 2019-04-14
    • 1970-01-01
    • 2022-01-08
    • 1970-01-01
    • 2014-01-03
    相关资源
    最近更新 更多