-
Notifications
You must be signed in to change notification settings - Fork 66
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
197 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# -*- coding: utf-8 -*- | ||
""" | ||
TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-用户管理(Bk-User) available. | ||
Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. | ||
Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at http://opensource.org/licenses/MIT | ||
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on | ||
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
specific language governing permissions and limitations under the License. | ||
""" | ||
from typing import Generator, List, Tuple | ||
|
||
from pydantic import BaseModel | ||
|
||
|
||
class TreeNode(BaseModel): | ||
id: str | ||
children: List["TreeNode"] = [] | ||
|
||
|
||
def build_forest_with_parent_relations(relations: List[Tuple[str, str]]) -> List[TreeNode]: | ||
"""根据提供的父子关系构建树/森林,父子关系结构:(node_id, parent_id)""" | ||
node_map = {node_id: TreeNode(id=node_id) for node_id, _ in relations} | ||
roots = [] | ||
for node_id, parent_id in relations: | ||
node = node_map[node_id] | ||
if not (parent_id and parent_id in node_map): | ||
roots.append(node) | ||
continue | ||
|
||
node_map[parent_id].children.append(node) | ||
|
||
return roots | ||
|
||
|
||
def bfs_traversal_forest(roots: List[TreeNode]) -> Generator[TreeNode, None, None]: | ||
"""广度优先遍历森林,确保父节点都在子节点之前""" | ||
queue = list(roots) | ||
while queue: | ||
node = queue.pop(0) | ||
yield node | ||
queue.extend(node.children) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
# -*- coding: utf-8 -*- | ||
""" | ||
TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-用户管理(Bk-User) available. | ||
Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. | ||
Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at http://opensource.org/licenses/MIT | ||
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on | ||
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
specific language governing permissions and limitations under the License. | ||
""" | ||
from typing import List, Tuple | ||
|
||
from bkuser.utils.tree import TreeNode, bfs_traversal_forest, build_forest_with_parent_relations | ||
|
||
|
||
def test_build_forest_with_tree_parent_relations(): | ||
"""理想情况,只有一棵树""" | ||
relations = [("A", ""), ("B", "A"), ("C", "A"), ("D", "B"), ("E", "B")] | ||
roots = build_forest_with_parent_relations(relations) | ||
assert roots == [ | ||
TreeNode( | ||
id="A", | ||
children=[ | ||
TreeNode( | ||
id="B", | ||
children=[TreeNode(id="D"), TreeNode(id="E")], | ||
), | ||
TreeNode(id="C"), | ||
], | ||
) | ||
] | ||
|
||
|
||
def test_build_forest_with_forest_parent_relations(): | ||
"""森林关系测试""" | ||
relations = [("A", ""), ("C", "B"), ("D", "B"), ("B", "")] | ||
roots = build_forest_with_parent_relations(relations) | ||
assert roots == [ | ||
TreeNode(id="A"), | ||
TreeNode(id="B", children=[TreeNode(id="C"), TreeNode(id="D")]), | ||
] | ||
|
||
|
||
def test_build_forest_with_invalid_parent_relations(): | ||
"""森林关系测试,但是某父节点丢失""" | ||
relations = [("A", ""), ("C", "B"), ("D", "B")] | ||
roots = build_forest_with_parent_relations(relations) | ||
assert roots == [TreeNode(id="A"), TreeNode(id="C"), TreeNode(id="D")] | ||
|
||
|
||
def test_build_forest_with_empty_parent_relations(): | ||
"""空关系测试""" | ||
relations: List[Tuple[str, str]] = [] | ||
roots = build_forest_with_parent_relations(relations) | ||
assert len(roots) == 0 | ||
|
||
|
||
def test_bfs_traversal_forest(): | ||
"""正常情况测试""" | ||
roots = [ | ||
TreeNode(id="A", children=[TreeNode(id="B"), TreeNode(id="C")]), | ||
TreeNode(id="D", children=[TreeNode(id="E")]), | ||
] | ||
nodes = list(bfs_traversal_forest(roots)) | ||
assert [n.id for n in nodes] == ["A", "D", "B", "C", "E"] | ||
|
||
|
||
def test_bfs_traversal_forest_empty(): | ||
"""空森林测试""" | ||
roots: List[TreeNode] = [] | ||
nodes = list(bfs_traversal_forest(roots)) | ||
assert nodes == [] | ||
|
||
|
||
def test_bfs_traversal_forest_single(): | ||
"""单个节点测试""" | ||
roots = [TreeNode(id="A")] | ||
nodes = list(bfs_traversal_forest(roots)) | ||
assert nodes == roots |