在Python中遍历二叉树

4
我即将完成一个项目,该项目要求我们创建一个利用二叉树结构的字典类。然而,我卡在了如何实现一个打印出树中所有元素的方法上,因为我对二叉树没有太多经验,所以编码时相当困惑。
我正在尝试解决的方法是一个keys方法,它将遍历整个树并返回所有键的列表。我认识的某个人暗示我应该创建一个私有的辅助函数,递归遍历树并跟踪所有键。我想创建他所说的内容,但我不知道如何编写代码。有人能帮我编写出这个方法吗?如果能解决这个问题,我的项目基本上就完成了。
以下是我到目前为止的代码。[Key:Value]对是元组。我已经编写了它,并从教科书示例中得到了一些帮助,构建了你在这里看到的所有内容:
class DictWithTree:

    def __init__(self):
        self._element = None
        self._left = None
        self._right = None
        self._size = 0

    def isempty(self):
        if self._element == None:
            return True
        return False

    def __len__(self):
        return self._size

    def __contains__(self,key):
        path = self._tracePath(key)
        return path[-1]._size > 0

    def _tracePath(self,key): # taken from the textbook example and modified
        if len(self) == 0 or key == self._element[0]:
            return [self]   
        elif len(key) < len(self._element[0]):
            return [self] + self._left._tracePath(key)
        else:
            return [self] + self._right._tracePath(key)

    def __getitem__(self,key):
        if len(self) == 0:
            raise KeyError(key)   
        elif key == self._element[0]:
            return self._element[1]
        elif key < self._element[0]:
            return self._left[key]
        elif key > self._element[0]:
            return self._right[key]
        else:
            raise KeyError(key)

    def __setitem__(self,key,value):
        path = self._tracePath(key)
        endOfPath = path[-1]
        if endOfPath._element != None:       
            if endOfPath._element[0] == key: 
                endOfPath._element = key,value
        if endOfPath._size == 0: # a new element
            for location in path:
                location._size += 1
            endOfPath._element = key,value
            endOfPath._left = DictWithTree()
            endOfPath._right = DictWithTree()

    def clear(self):
        self._element = None
        self._left = None
        self._right = None
        self._size = 0

    def pop(self,key):
        value = self[key]
        self._remove(key)
        return value

    def popitem(self):     # returns the 'last' item in the dictionary,
        if self.isempty(): # (i.e. the largest key in the dictionary)
            return KeyError("There are no keys in the dictionary")
        elif self._right._element == None:
            return self._element
        else:   
            return self._right.popitem()                                   

    def _remove(self,key):
        path = self._tracePath(key)
        endOfPath = path[-1]
        if endOfPath._size > 0:
            for location in path:
                location._size -= 1
            if len(endOfPath._left) == 0:
                endOfPath._promoteChild(endOfPath._right)
            elif len(endOfPath._right) == 0:
                endOfPath._promoteChild(endOfPath._left)
            else:
                endOfPath._element = endOfPath._left.pop()

    def _promoteChild(self,child):
        self._element = child._element
        self._left = child._left
        self._right = child._right

1
你为什么要使用自己编写的树而不是Python字典? - Will
这是一个项目,我们应该做的就是创建一个使用二叉树的字典。 - Eric
我相信他说过那是他的项目规格说明书。 - ninjagecko
如果有人能帮忙,那就是救星了。我还有其他方法要实现,但它们都源于完成这个keys方法。可能只有10-12行代码阻止我完成 :D - Eric
@Eric 你应该接受这些答案中的一个,或者如果这些答案都不是你所需要的,那么请发布你的最终解决方案... 当然,我假设你已经完成了你的项目? - Jonathan
4个回答

3

使用Python 3的yield from,可以实现树的中序遍历递归解决方案:

def traverse(tree):
    if tree.left is not None:
        yield from traverse(tree.left)

    yield tree.value

    if tree.right is not None:
        yield from traverse(tree.right)

print(list(traverse(tree_root)))

我认为这种方法更易读、概念上更简单。希望对某些人有所帮助。


2

您需要做的是创建一个辅助方法visitAllSubnodes(node),它对当前节点执行某些操作,然后递归地调用左右子节点。 visitAllSubnodes(node)可以执行任何操作,在您的情况下,它可以像print(node._element)一样,但您可以使您的函数非常模块化,例如

def visitAllSubnodes(node, whatToDoAtEachNode):
    whatToDoAtEachNode(node)
    visitAllSubnodes(node._left, whatToDoAtEachNode)
    visitAllSubnodes(node._right, whatToDoAtEachNode)

def printAllElements(node):
    visitAllSubnodes(node, lambda x:print(x))

要实际返回某些东西,您需要使用高阶函数和闭包的概念。例如,您可以创建一个函数来定义私有个人累加器(您添加到其中的列表),以及另一个函数,该函数属于该私有个人累加器,然后返回该函数。
例如,每次遍历树时,您都可以调用此高阶函数,称其为makeFunctionWhichKeepsTrackOfWhatHasBeenPassedIntoIt(),它返回一个函数,该函数跟踪已传递给它的内容以及累加器。我可以提供更多信息,但那将是问题集的重点。 =)

0

这应该可以做到

def allKeys(d):
    toVisit = []
    toVisit.append(d)
    while (len(toVisit)>0):
        x = toVisit.pop()
        if x._element:
            yield x._element[0]
        if x._left:
            toVisit.append(x._left)
        if x._right:
            toVisit.append(x._right)

对于顺序遍历,您需要一个递归解决方案,如维基百科entry中所述的树遍历。


如果你想让它返回一个包含所有键的列表,应该怎么做呢?我以前也从未使用过“yield”或生成器。现在,它只是打印出“<generator object allKeys at 0x02BAE5F8>”。 - Eric
最后一条评论上的NM,我已经解决了。不过,有什么办法可以让它按顺序打印吗? 它似乎按照以下顺序进行:根 ---> 大于根的键 ---> 小于根的键 - Eric

0

对于树的遍历,通常人们使用广度优先遍历(BFT)或深度优先遍历(DFT)。

在BFT中,您使用队列来记住您离开的位置,在DFT中,您使用堆栈来记住您离开的位置。如果您了解队列和堆栈的性质,那么理解BFT和DFT就轻而易举了。否则,请阅读广度优先搜索深度优先搜索。顺便说一下,遍历树的代码通常不超过10行,这证明它们是多么容易。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接