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

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

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

一、安装treelib

pip install treelib

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

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

1. 创建一棵多叉树

# coding=utf-8
from treelib import Tree, Node


tree = Tree()
tree.show()
print(tree.identifier)

运行结果:

Tree is empty

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. 添加节点到多叉树中

tree.create_node(tag='Node-5', identifier='node-5', data=5)
tree.create_node(tag='Node-10', identifier='node-10', parent='node-5', data=10)
tree.create_node('Node-15', 'node-15', 'node-10', 15)
tree.show()

node = Node(data=50)
tree.add_node(node, parent='node-5')
node_a = Node(tag='Node-A', identifier='node-A', data='A')
tree.add_node(node_a, parent='node-5')
tree.show()

运行结果:

Node-5
└── Node-10
    └── Node-15

Node-5
├── Node-10
│   └── Node-15
├── Node-A
└── 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. 节点的属性和方法

print(node)
print('node id: ', node.identifier)
print('node tag:', node.tag)
print('node data:', node.data)

print('node is leaf: ', node.is_leaf())
print('node is root: ', node.is_root())

运行结果:

Node(tag=719f3842-e7af-11ea-8caa-b886873e4844, identifier=719f3842-e7af-11ea-8caa-b886873e4844, data=50)
node id:  719f3842-e7af-11ea-8caa-b886873e4844
node tag: 719f3842-e7af-11ea-8caa-b886873e4844
node data: 50
node is leaf:  True
node is root:  False

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

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

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

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

四、Tree中的方法介绍

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

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

运行结果:

Node-5
├── 6e75a77a-e92d-11ea-abfe-b886873e4844
├── Node-10
│   └── Node-15
└── Node-A

tree len:  5
tree size: 5
tree size: 7

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

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

2. 返回多叉树中的节点

tree.show()
print(tree.all_nodes())
for node in tree.all_nodes_itr():
    print(node)
for id in tree.expand_tree():
    print(id)

运行结果:

Node-5
├── Node-10
│   ├── Node-15
│   │   └── Node-30
│   └── Node-20
├── Node-A
└── fcb7619a-e931-11ea-8946-b886873e4844

[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)]
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)
node-5
node-10
node-15
node-30
node-20
node-A
fcb7619a-e931-11ea-8946-b886873e4844

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

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

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

3. 多叉树中的节点关系

print('node-5 children:', tree.children('node-5'))
print('node-10 branch:', tree.is_branch('node-10'))
print('node-20 siblings:', tree.siblings('node-20'))
print('node-30 parent:', tree.parent('node-30'))
print('node-15 ancestor: ', tree.ancestor('node-15'))
print(tree.is_ancestor('node-15', 'node-30'))
for node in tree.rsearch('node-30'):
    print(node)

运行结果:

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)]
node-10 branch: ['node-15', 'node-20']
node-20 siblings: [Node(tag=Node-15, identifier=node-15, data=15)]
node-30 parent: Node(tag=Node-15, identifier=node-15, data=15)
node-15 ancestor:  node-10
True
node-30
node-15
node-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. 多叉树的深度和叶子节点

print('tree depth:', tree.depth())
print('node-20 depth:', tree.depth(node='node-20'))
print('node-20 level:', tree.level('node-20'))
print('tree leaves:', tree.leaves())
print(tree.paths_to_leaves())

运行结果:

tree depth: 3
node-20 depth: 2
node-20 level: 2
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)]
[['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. 判断节点是否在多叉树中和获取节点

print('node-10 is in tree:', tree.contains('node-10'))
print('node-100 is in tree:', tree.contains('node-100'))
print(tree.get_node('node-10'))
print(tree.get_node('node-100'))
tree.update_node('node-20', data=200)
print('data of node-20:', tree.get_node('node-20').data)

运行结果:

node-10 is in tree: True
node-100 is in tree: False
Node(tag=Node-10, identifier=node-10, data=10)
None
data of node-20: 200

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

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

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

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

tree.show()
tree.link_past_node('node-10')
tree.show()
tree.move_node('node-30', 'node-20')
tree.show()

运行结果:

Node-5
├── 488e66b6-e939-11ea-aa02-b886873e4844
├── Node-10
│   ├── Node-15
│   │   └── Node-30
│   └── Node-20
└── Node-A

Node-5
├── 488e66b6-e939-11ea-aa02-b886873e4844
├── Node-15
│   └── Node-30
├── Node-20
└── Node-A

Node-5
├── 488e66b6-e939-11ea-aa02-b886873e4844
├── Node-15
├── Node-20
│   └── Node-30
└── Node-A

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

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

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

tree2 = Tree()
tree2.create_node(tag='Node-7', identifier='node-7', data=7)
tree2.create_node(tag='Node-17', identifier='node-17', parent='node-7', data=17)
tree2.show()
tree.paste('node-20', tree2)
tree.show()

tree3 = Tree()
tree3.create_node(tag='Node-8', identifier='node-8', data=8)
tree3.create_node(tag='Node-18', identifier='node-18', parent='node-8', data=18)
tree3.show()
tree.merge('node-A', tree3)
tree.show()

print(tree.subtree('node-20'))

运行结果:

Node-7
└── Node-17

Node-5
├── 4f603236-e93a-11ea-8577-b886873e4844
├── Node-15
├── Node-20
│   ├── Node-30
│   └── Node-7
│       └── Node-17
└── Node-A

Node-8
└── Node-18

Node-5
├── 4f603236-e93a-11ea-8577-b886873e4844
├── Node-15
├── Node-20
│   ├── Node-30
│   └── Node-7
│       └── Node-17
└── Node-A
    └── Node-18

Node-20
├── Node-30
└── Node-7
    └── Node-17

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

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

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

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

print(tree.to_dict())
print(tree.to_json())
tree.to_graphviz()
tree.save2file('demo_tree.tree')

运行结果:

{'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']}}
{"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"]}}
digraph tree {
	"node-5" [label="Node-5", shape=circle]
	"node-15" [label="Node-15", shape=circle]
	"node-20" [label="Node-20", shape=circle]
	"node-A" [label="Node-A", shape=circle]
	"e1f2ba34-e93b-11ea-a5f9-b886873e4844" [label="e1f2ba34-e93b-11ea-a5f9-b886873e4844", shape=circle]
	"node-30" [label="Node-30", shape=circle]
	"node-7" [label="Node-7", shape=circle]
	"node-18" [label="Node-18", shape=circle]
	"node-17" [label="Node-17", shape=circle]
	"node-5" -> "e1f2ba34-e93b-11ea-a5f9-b886873e4844"
	"node-5" -> "node-A"
	"node-5" -> "node-15"
	"node-5" -> "node-20"
	"node-20" -> "node-30"
	"node-20" -> "node-7"
	"node-A" -> "node-18"
	"node-7" -> "node-17"
}

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

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

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

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

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

9. 多叉树删除子树

tree.show()
print('remover node: ', tree.remove_node('node-7'))
tree.show()
print(tree.remove_subtree('node-20'))
tree.show()

运行结果:

Node-5
├── 9e0bce40-e93d-11ea-99e7-b886873e4844
├── Node-15
├── Node-20
│   ├── Node-30
│   └── Node-7
│       └── Node-17
└── Node-A
    └── Node-18

remover node:  2
Node-5
├── 9e0bce40-e93d-11ea-99e7-b886873e4844
├── Node-15
├── Node-20
│   └── Node-30
└── Node-A
    └── Node-18

Node-20
└── Node-30

Node-5
├── 9e0bce40-e93d-11ea-99e7-b886873e4844
├── Node-15
└── Node-A
    └── Node-18

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

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

 

 

  • 25
    点赞
  • 89
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小斌哥ge

非常感谢,祝你一切顺利。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值