当前位置:   article > 正文

Python treelib库创建多叉树的用法介绍_import treenode

import treenode

Python treelib库创建多叉树的用法介绍

treelib 库是一个 Python 的第三方库。这个库实现了一些多叉树相关的常用方法。

一、安装treelib

pip install treelib

在 treelib 库中,实现了两个类 Tree 和 Node,分别用于创建多叉树和创建节点。

二、创建多叉树和添加节点

1. 创建一棵多叉树

  1. # coding=utf-8
  2. from treelib import Tree, Node
  3. tree = Tree()
  4. tree.show()
  5. print(tree.identifier)

运行结果:

  1. Tree is empty
  2. 2f9fa5c8-e7aa-11ea-9b8b-b886873e4844

Tree 类用于实例化一棵多叉树。有四个初始化参数(tree=None, deep=False, node_class=None, identifier=None),都有默认值。tree表示拷贝一棵已有的树,传入一个Tree的对象。deep表示拷贝另一棵树时是否深拷贝。node_class表示节点类,一般不需要使用,可以不管。identifier表示树的id,在初始化时会默认分配一个唯一的id值,也可以手动指定一个id,保证是唯一的就行,树一旦创建完成,id就不能再修改。

2. 添加节点到多叉树中

  1. tree.create_node(tag='Node-5', identifier='node-5', data=5)
  2. tree.create_node(tag='Node-10', identifier='node-10', parent='node-5', data=10)
  3. tree.create_node('Node-15', 'node-15', 'node-10', 15)
  4. tree.show()
  5. node = Node(data=50)
  6. tree.add_node(node, parent='node-5')
  7. node_a = Node(tag='Node-A', identifier='node-A', data='A')
  8. tree.add_node(node_a, parent='node-5')
  9. tree.show()

运行结果:

  1. Node-5
  2. └── Node-10
  3. └── Node-15
  4. Node-5
  5. ├── Node-10
  6. │ └── Node-15
  7. ├── Node-A
  8. └── ddeb9b02-e7ab-11ea-b2ee-b886873e4844

create_node(tag=None, identifier=None, parent=None, data=None): 创建一个节点并直接添加到树中。tag表示节点的标签,在控制台打印树的结构时显示的就是节点的标签,可以指定值,如果不指定值则默认等于id。identifier表示节点的id,默认会分配一个唯一的id,也可以指定一个唯一id。这里要注意,id是唯一的,不能重复,标签是可以重复的但最好别重复。parent表示节点的父节点,根节点可以不指定,不过,一棵树只能有一个根节点,如果树中已经有根节点了,parent还为空会报错。data表示节点中保存的数据,可以是各种数据类型。

add_node(node, parent=None): 添加一个节点到树中。这个方法需要先用 Node 类创建好节点,第一个参数传入节点,第二参数同create_node()方法。

三、Node创建节点和Node类中的方法

1. 创建节点

Node 类用于创建节点,有四个初始化参数(tag=None, identifier=None, expanded=True, data=None),都有默认值。tag、identifier和data同前面的create_node()方法。expanded表示节点的可扩展性,在 Tree 中会使用到,可以不用管,保持默认就行。

Node 类创建节点一般和 Tree 类中的add_node()配合使用。

2. 节点的属性和方法

  1. print(node)
  2. print('node id: ', node.identifier)
  3. print('node tag:', node.tag)
  4. print('node data:', node.data)
  5. print('node is leaf: ', node.is_leaf())
  6. print('node is root: ', node.is_root())

运行结果:

  1. Node(tag=719f3842-e7af-11ea-8caa-b886873e4844, identifier=719f3842-e7af-11ea-8caa-b886873e4844, data=50)
  2. node id: 719f3842-e7af-11ea-8caa-b886873e4844
  3. node tag: 719f3842-e7af-11ea-8caa-b886873e4844
  4. node data: 50
  5. node is leaf: True
  6. node is root: False

直接打印节点,会将节点作为一个类对象打印出来。节点的tag、identifier和data三条属性可以单独调用,可以看到在不指定值时,tag与identifier相等,是一个系统分配的唯一值。

is_leaf(tree_id=None): 返回节点在树中的位置是不是叶节点。

is_root(tree_id=None): 返回节点在树中的位置是不是根节点。

Node 类中还有一些其他的方法,主要用于对节点的指针作处理,一般不会直接调用,这里就不介绍了。

四、Tree中的方法介绍

1. 返回多叉树中的节点个数

  1. tree.show()
  2. print('tree len: ', len(tree))
  3. print('tree size:', tree.size())
  4. tree.create_node(tag='Node-20', identifier='node-20', parent='node-10', data=20)
  5. tree.create_node(tag='Node-30', identifier='node-30', parent='node-15', data=30)
  6. print('tree size:', tree.size())

运行结果:

  1. Node-5
  2. ├── 6e75a77a-e92d-11ea-abfe-b886873e4844
  3. ├── Node-10
  4. │ └── Node-15
  5. └── Node-A
  6. tree len: 5
  7. tree size: 5
  8. tree size: 7

show(): 将多叉树按树形结构展示输出。可以传入相关参数来限定展示的范围。

size(level=None): 返回多叉树的节点个数,默认返回多叉树中的所有节点,与len()方法相同。如果指定层数level,则只返回该层的节点个数。

2. 返回多叉树中的节点

  1. tree.show()
  2. print(tree.all_nodes())
  3. for node in tree.all_nodes_itr():
  4. print(node)
  5. for id in tree.expand_tree():
  6. print(id)

运行结果:

  1. Node-5
  2. ├── Node-10
  3. │ ├── Node-15
  4. │ │ └── Node-30
  5. │ └── Node-20
  6. ├── Node-A
  7. └── fcb7619a-e931-11ea-8946-b886873e4844
  8. [Node(tag=Node-5, identifier=node-5, data=5), Node(tag=Node-10, identifier=node-10, data=10), Node(tag=Node-15, identifier=node-15, data=15), Node(tag=fcb7619a-e931-11ea-8946-b886873e4844, identifier=fcb7619a-e931-11ea-8946-b886873e4844, data=50), Node(tag=Node-A, identifier=node-A, data=A), Node(tag=Node-20, identifier=node-20, data=20), Node(tag=Node-30, identifier=node-30, data=30)]
  9. Node(tag=Node-5, identifier=node-5, data=5)
  10. Node(tag=Node-10, identifier=node-10, data=10)
  11. Node(tag=Node-15, identifier=node-15, data=15)
  12. Node(tag=fcb7619a-e931-11ea-8946-b886873e4844, identifier=fcb7619a-e931-11ea-8946-b886873e4844, data=50)
  13. Node(tag=Node-A, identifier=node-A, data=A)
  14. Node(tag=Node-20, identifier=node-20, data=20)
  15. Node(tag=Node-30, identifier=node-30, data=30)
  16. node-5
  17. node-10
  18. node-15
  19. node-30
  20. node-20
  21. node-A
  22. fcb7619a-e931-11ea-8946-b886873e4844

all_nodes(): 返回多叉树中的所有节点,返回结果是一个节点对象构成的列表,节点的顺序是添加到树中的顺序。

all_nodes_itr(): 返回多叉树中的所有节点,返回结果是一个迭代器,节点的顺序是添加到树中的顺序。

expand_tree(): 返回多叉树中的所有节点id,返回结果是一个生成器,顺序是按深度优先遍历的顺序。可以传入过滤条件等参数来改变返回的生成器。

3. 多叉树中的节点关系

  1. print('node-5 children:', tree.children('node-5'))
  2. print('node-10 branch:', tree.is_branch('node-10'))
  3. print('node-20 siblings:', tree.siblings('node-20'))
  4. print('node-30 parent:', tree.parent('node-30'))
  5. print('node-15 ancestor: ', tree.ancestor('node-15'))
  6. print(tree.is_ancestor('node-15', 'node-30'))
  7. for node in tree.rsearch('node-30'):
  8. print(node)

运行结果:

  1. node-5 children: [Node(tag=Node-10, identifier=node-10, data=10), Node(tag=2fbce8a8-e933-11ea-9304-b886873e4844, identifier=2fbce8a8-e933-11ea-9304-b886873e4844, data=50), Node(tag=Node-A, identifier=node-A, data=A)]
  2. node-10 branch: ['node-15', 'node-20']
  3. node-20 siblings: [Node(tag=Node-15, identifier=node-15, data=15)]
  4. node-30 parent: Node(tag=Node-15, identifier=node-15, data=15)
  5. node-15 ancestor: node-10
  6. True
  7. node-30
  8. node-15
  9. node-10
  10. node-5

children(nid): 传入节点id,返回节点的所有子节点,返回结果是一个节点列表,如果没有子节点则返回空列表。

is_branch(nid): 传入节点id,返回节点的所有子节点id,返回结果是一个列表,如果没有子节点则返回空列表。

siblings(nid): 传入节点id,返回节点的所有兄弟节点,返回结果是一个节点列表,如果没有兄弟节点则返回空列表。

parent(nid): 传入节点id,返回节点的父节点,如果传入的是根节点,则返回None。

ancestor(nid, level=None): 传入节点id,返回节点的一个祖先节点,如果指定level则返回level层的祖先节点,如果不指定level则返回父节点,如果指定level比节点的层数大,则报错。

is_ancestor(ancestor, grandchild): 传入两个节点id,判断ancestor是不是grandchild的祖先,返回布尔值。

rsearch(nid, filter=None): 传入节点id,遍历节点到根节点的路径上的所有节点id,包含该节点和根节点,返回结果是一个生成器。可以传入过滤条件对结果进行过滤。

4. 多叉树的深度和叶子节点

  1. print('tree depth:', tree.depth())
  2. print('node-20 depth:', tree.depth(node='node-20'))
  3. print('node-20 level:', tree.level('node-20'))
  4. print('tree leaves:', tree.leaves())
  5. print(tree.paths_to_leaves())

运行结果:

  1. tree depth: 3
  2. node-20 depth: 2
  3. node-20 level: 2
  4. tree leaves: [Node(tag=7e421cb4-e935-11ea-8e6d-b886873e4844, identifier=7e421cb4-e935-11ea-8e6d-b886873e4844, data=50), Node(tag=Node-A, identifier=node-A, data=A), Node(tag=Node-20, identifier=node-20, data=20), Node(tag=Node-30, identifier=node-30, data=30)]
  5. [['node-5', '7e421cb4-e935-11ea-8e6d-b886873e4844'], ['node-5', 'node-A'], ['node-5', 'node-10', 'node-20'], ['node-5', 'node-10', 'node-15', 'node-30']]

depth(node=None): 返回节点的高度,根节点高度为0,依次递增。如果不指定节点则返回树的高度。

level(nid, filter=None): 返回节点的高度,level()与depth()的结果相同,只是传参方式不一样。

leaves(nid=None): 返回多叉树的所有叶节点,返回结果是一个节点列表。不指定节点id时,默认返回整棵树的所有叶节点,指定节点id时,返回以指定节点作为根节点的子树的所有叶节点。

paths_to_leaves(): 返回根节点到每个叶节点的路径上的所有节点id,每个叶节点的结果是一个列表,所有叶节点的结果又组成一个列表。所以最终结果是列表嵌套列表。

5. 判断节点是否在多叉树中和获取节点

  1. print('node-10 is in tree:', tree.contains('node-10'))
  2. print('node-100 is in tree:', tree.contains('node-100'))
  3. print(tree.get_node('node-10'))
  4. print(tree.get_node('node-100'))
  5. tree.update_node('node-20', data=200)
  6. print('data of node-20:', tree.get_node('node-20').data)

运行结果:

  1. node-10 is in tree: True
  2. node-100 is in tree: False
  3. Node(tag=Node-10, identifier=node-10, data=10)
  4. None
  5. data of node-20: 200

contains(nid): 传入节点id,判断节点是否在树中,返回布尔值。

get_node(nid): 传入节点id,从树中获取节点,返回节点对象,如果传入的节点id不在树中则返回None。

update_node(nid, **attrs): 传入节点id,修改节点的属性值,需要修改哪个参数就用关键字参数的方式传入,可以传入0个或多个属性。

6. 调整多叉树中的节点关系

  1. tree.show()
  2. tree.link_past_node('node-10')
  3. tree.show()
  4. tree.move_node('node-30', 'node-20')
  5. tree.show()

运行结果:

  1. Node-5
  2. ├── 488e66b6-e939-11ea-aa02-b886873e4844
  3. ├── Node-10
  4. │ ├── Node-15
  5. │ │ └── Node-30
  6. │ └── Node-20
  7. └── Node-A
  8. Node-5
  9. ├── 488e66b6-e939-11ea-aa02-b886873e4844
  10. ├── Node-15
  11. │ └── Node-30
  12. ├── Node-20
  13. └── Node-A
  14. Node-5
  15. ├── 488e66b6-e939-11ea-aa02-b886873e4844
  16. ├── Node-15
  17. ├── Node-20
  18. │ └── Node-30
  19. └── Node-A

link_past_node(nid): 传入节点id,将该节点的所有子节点都链接到它的父节点上,相当于将该节点从树中删除。如果节点id不在树中则报错。

move_node(source, destination): 传入两个节点id,将source节点移动成为destination节点的子节点。如果节点id不在树中则报错。

7. 多叉树的合并和子树拷贝

  1. tree2 = Tree()
  2. tree2.create_node(tag='Node-7', identifier='node-7', data=7)
  3. tree2.create_node(tag='Node-17', identifier='node-17', parent='node-7', data=17)
  4. tree2.show()
  5. tree.paste('node-20', tree2)
  6. tree.show()
  7. tree3 = Tree()
  8. tree3.create_node(tag='Node-8', identifier='node-8', data=8)
  9. tree3.create_node(tag='Node-18', identifier='node-18', parent='node-8', data=18)
  10. tree3.show()
  11. tree.merge('node-A', tree3)
  12. tree.show()
  13. print(tree.subtree('node-20'))

运行结果:

  1. Node-7
  2. └── Node-17
  3. Node-5
  4. ├── 4f603236-e93a-11ea-8577-b886873e4844
  5. ├── Node-15
  6. ├── Node-20
  7. │ ├── Node-30
  8. │ └── Node-7
  9. │ └── Node-17
  10. └── Node-A
  11. Node-8
  12. └── Node-18
  13. Node-5
  14. ├── 4f603236-e93a-11ea-8577-b886873e4844
  15. ├── Node-15
  16. ├── Node-20
  17. │ ├── Node-30
  18. │ └── Node-7
  19. │ └── Node-17
  20. └── Node-A
  21. └── Node-18
  22. Node-20
  23. ├── Node-30
  24. └── Node-7
  25. └── Node-17

paste(nid, new_tree, deep=False): 传入节点id和一棵新树,将整棵新树粘贴成为指定节点的子树,新树的根节点成为指定节点的子节点。如果节点不在树中则报错。

merge(nid, new_tree, deep=False): 传入节点id和一棵新树,将新树与指定节点进行合并,合并后新树的根节点不保留,新树中根节点的子树全部成为指定节点的子树。如果节点不在树中则报错。

subtree(nid, identifier=None): 传入节点id,拷贝以该节点作为根节点的子树。如果节点不在树中则报错。

8. 多叉树转换成字典和保存到文件中

  1. print(tree.to_dict())
  2. print(tree.to_json())
  3. tree.to_graphviz()
  4. tree.save2file('demo_tree.tree')

运行结果:

  1. {'Node-5': {'children': ['Node-15', {'Node-20': {'children': ['Node-30', {'Node-7': {'children': ['Node-17']}}]}}, {'Node-A': {'children': ['Node-18']}}, 'e1f2ba34-e93b-11ea-a5f9-b886873e4844']}}
  2. {"Node-5": {"children": ["Node-15", {"Node-20": {"children": ["Node-30", {"Node-7": {"children": ["Node-17"]}}]}}, {"Node-A": {"children": ["Node-18"]}}, "e1f2ba34-e93b-11ea-a5f9-b886873e4844"]}}
  3. digraph tree {
  4. "node-5" [label="Node-5", shape=circle]
  5. "node-15" [label="Node-15", shape=circle]
  6. "node-20" [label="Node-20", shape=circle]
  7. "node-A" [label="Node-A", shape=circle]
  8. "e1f2ba34-e93b-11ea-a5f9-b886873e4844" [label="e1f2ba34-e93b-11ea-a5f9-b886873e4844", shape=circle]
  9. "node-30" [label="Node-30", shape=circle]
  10. "node-7" [label="Node-7", shape=circle]
  11. "node-18" [label="Node-18", shape=circle]
  12. "node-17" [label="Node-17", shape=circle]
  13. "node-5" -> "e1f2ba34-e93b-11ea-a5f9-b886873e4844"
  14. "node-5" -> "node-A"
  15. "node-5" -> "node-15"
  16. "node-5" -> "node-20"
  17. "node-20" -> "node-30"
  18. "node-20" -> "node-7"
  19. "node-A" -> "node-18"
  20. "node-7" -> "node-17"
  21. }

to_dict(): 将树转换成字典结构,兄弟节点用列表表示成并列关系,父子节点用字典表示成键值关系。最后将转换结果返回。

to_json(): 将树转化成json,数据的结构与to_dict()的相同,只是格式不一样。最后将转换结果返回。

to_graphviz(): 将树转化成可视化图形的结构,无返回值。

save2file(filename): 将树保存到一个指定文件中,运行后会在当前路径下生成一个filename文件,在文件中写入树的结构图,同名文件存在时会追加写入,不会覆盖。

这四个方法都有很多关键字参数,可以指定参数来改变转化的结果。

9. 多叉树删除子树

  1. tree.show()
  2. print('remover node: ', tree.remove_node('node-7'))
  3. tree.show()
  4. print(tree.remove_subtree('node-20'))
  5. tree.show()

运行结果:

  1. Node-5
  2. ├── 9e0bce40-e93d-11ea-99e7-b886873e4844
  3. ├── Node-15
  4. ├── Node-20
  5. │ ├── Node-30
  6. │ └── Node-7
  7. │ └── Node-17
  8. └── Node-A
  9. └── Node-18
  10. remover node: 2
  11. Node-5
  12. ├── 9e0bce40-e93d-11ea-99e7-b886873e4844
  13. ├── Node-15
  14. ├── Node-20
  15. │ └── Node-30
  16. └── Node-A
  17. └── Node-18
  18. Node-20
  19. └── Node-30
  20. Node-5
  21. ├── 9e0bce40-e93d-11ea-99e7-b886873e4844
  22. ├── Node-15
  23. └── Node-A
  24. └── Node-18

remove_node(identifier): 传入节点id,将该节点作为根节点的子树删除,返回删除节点的个数。

remove_subtree(nid, identifier=None): 传入节点id,将该节点作为根节点的子树删除,返回删除的子树。

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/528321
推荐阅读
相关标签
  

闽ICP备14008679号