Skip to content

Instantly share code, notes, and snippets.

@neuromaancer
Forked from upsuper/tree.md
Last active September 6, 2019 13:43
Show Gist options
  • Save neuromaancer/b2f6cd23d94d6ef6a3ff5abc6a026aed to your computer and use it in GitHub Desktop.
Save neuromaancer/b2f6cd23d94d6ef6a3ff5abc6a026aed to your computer and use it in GitHub Desktop.
一行 Python 实现树

一行 Python 实现树

使用 Python 内置的 defaultdict,我们可以很容易的定义一个树形数据结构:

def tree(): return defaultdict(tree)

就是这样!

简单地来说,一颗树就是一个默认值是其子树的字典。

(使用之前需要确认已经 from collections import defaultdict 了)

(另: Hacker News 读者 @zbuc 指出这种结构被称为 autovivification。 太酷了!)

例子

JSON 风格

现在我们可以创建一个 JSON 风格的嵌套字典,我们不需要显式地创建子字典——当我们需要时,它们神奇地自动出现了:

users = tree()
users['harold']['username'] = 'hrldcpr'
users['handler']['username'] = 'matthandlersux'

我们可以将这些用 print(json.dumps(users)) 以 JSON 的形式输出,于是我们得到:

{"harold": {"username": "hrldcpr"}, "handler": {"username": "matthandlersux"}}

不需要赋值

我们甚至可以不需要任何赋值就可以创建整个树结构:

taxonomy = tree()
taxonomy['Animalia']['Chordata']['Mammalia']['Carnivora']['Felidae']['Felis']['cat']
taxonomy['Animalia']['Chordata']['Mammalia']['Carnivora']['Felidae']['Panthera']['lion']
taxonomy['Animalia']['Chordata']['Mammalia']['Carnivora']['Canidae']['Canis']['dog']
taxonomy['Animalia']['Chordata']['Mammalia']['Carnivora']['Canidae']['Canis']['coyote']
taxonomy['Plantae']['Solanales']['Solanaceae']['Solanum']['tomato']
taxonomy['Plantae']['Solanales']['Solanaceae']['Solanum']['potato']
taxonomy['Plantae']['Solanales']['Convolvulaceae']['Ipomoea']['sweet potato']

我们接下来将漂亮地输出他们,不过需要先将他们转换为标准的字典:

def dicts(t): return {k: dicts(t[k]) for k in t}

现在我们用 pprint(dicts(taxonomy)) 来漂亮地输出结构:

{'Animalia': {'Chordata': {'Mammalia': {'Carnivora': {'Canidae': {'Canis': {'coyote': {},
                                                                            'dog': {}}},
                                                      'Felidae': {'Felis': {'cat': {}},
                                                                  'Panthera': {'lion': {}}}}}}},
 'Plantae': {'Solanales': {'Convolvulaceae': {'Ipomoea': {'sweet potato': {}}},
                           'Solanaceae': {'Solanum': {'potato': {},
                                                      'tomato': {}}}}}}

于是我们引用到的子结构以字典的形式存在,空字典即代表了叶子。

迭代

这棵树可以很欢乐地被迭代处理,同样因为只要简单地引用一个结构它就会出现。

举例来说,假设我们想要解析一个新动物的列表,将它们加入我们上面的 taxonomy,我们只要这样调用一个函数:

add(taxonomy,
    'Animalia,Chordata,Mammalia,Cetacea,Balaenopteridae,Balaenoptera,blue whale'.split(','))

我们可以简单地这样实现它:

def add(t, keys):
  for key in keys:
    t = t[key]

再一次,我们完全没有对字典使用任何赋值,仅仅是引用了这些键,我们便创建了我们新的结构:

{'Animalia': {'Chordata': {'Mammalia': {'Carnivora': {'Canidae': {'Canis': {'coyote': {},
                                                                            'dog': {}}},
                                                      'Felidae': {'Felis': {'cat': {}},
                                                                  'Panthera': {'lion': {}}}},
                                        'Cetacea': {'Balaenopteridae': {'Balaenoptera': {'blue whale': {}}}}}}},
 'Plantae': {'Solanales': {'Convolvulaceae': {'Ipomoea': {'sweet potato': {}}},
                           'Solanaceae': {'Solanum': {'potato': {},
                                                      'tomato': {}}}}}}

结论

这也许并不特别实用,而且也出现了一些令人困惑的代码 (见上面的 add())。

不过如果你喜欢 Python,我希望思考这些会让你觉得有趣,或者值得去理解。

class Node:
    def __init__(self, val):
        self.l = None
        self.r = None
        self.v = val

class Tree:
    def __init__(self):
        self.root = None

    def getRoot(self):
        return self.root

    def add(self, val):
        if(self.root is None):
            self.root = Node(val)
        else:
            self._add(val, self.root)

    def _add(self, val, node):
        if(val < node.v):
            if(node.l is not None):
                self._add(val, node.l)
            else:
                node.l = Node(val)
        else:
            if(node.r is not None):
                self._add(val, node.r)
            else:
                node.r = Node(val)

    def find(self, val):
        if(self.root is not None):
            return self._find(val, self.root)
        else:
            return None

    def _find(self, val, node):
        if(val == node.v):
            return node
        elif(val < node.v and node.l is not None):
            self._find(val, node.l)
        elif(val > node.v and node.r is not None):
            self._find(val, node.r)

    def deleteTree(self):
        # garbage collector will do this for us. 
        self.root = None

    def printTree(self):
        if(self.root is not None):
            self._printTree(self.root)

    def _printTree(self, node):
        if(node is not None):
            self._printTree(node.l)
            print str(node.v) + ' '
            self._printTree(node.r)

#     3
# 0     4
#   2      8
tree = Tree()
tree.add(3)
tree.add(4)
tree.add(0)
tree.add(8)
tree.add(2)
tree.printTree()
print (tree.find(3)).v
print tree.find(10)
tree.deleteTree()
tree.printTree()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment