建立组织结构图

时间:2019-02-05 07:23:22

标签: python python-3.x algorithm graph

我在编写可读取员工/经理CSV的算法并输出包含员工/经理关系的有向图时遇到麻烦。

我的foobar示例:给出以下CSV文件

john,
jill, john
tom, john
tim, jill
felisa, tom
ray, tom
bob, tim
jim, tim
pam, felisa
ben, ray
james, ray
mike, pam
rashad, ben
henry, james

如何构建DiGraph以便可以显示以下组织结构:

         john
        /    \
     jill    tom
     /       /  \
   tim   felisa  ray
 /  \      /     /  \
bob jim   pam  ben  james
          /     /       \
        mike   rashad    henry

显然这是一个图形问题,但是我在决定使用哪种结构时遇到了麻烦(例如,最好使用dict还是构建自定义OrganizationalGraph对象等)。任何帮助表示赞赏。

选择的语言并不是很重要(尽管我们可以简单地说Python [相应地更新标签]),但我更想尝试理解此类问题的基础(例如,递归vs.迭代,使用set()来存储经理的直接报告,而不是仅使用 抽象数据结构)。 最后,不,使用标准库之外的任何软件包都是初学者。

2 个答案:

答案 0 :(得分:4)

要构建图,我们将获得以下信息:

  1. 根(在本例中为John)
  2. 形式(孩子,父母)的边列表
  3. 每个节点最多有两个子节点(从您的示例中推断出,但是下面的代码适用于具有任意数量的子节点的任何节点)

请注意,在您的问题示例csv中,您似乎将felisa拼写为felia。结果,这些输出不是您输入的实际数据,而是校正后的版本。 首先,我们解析csv文件,提取根和边列表:

import csv

with open('data.csv') as f:
    f = list(csv.reader(f, skipinitialspace=True))
    root, *edges = f
    root = root[0]

print(root)
print(edges)

输出:

john 
[['jill', 'john'], ['tom', 'john'], ['tim', 'jill'], ['felisa', 'tom'], ['ray', 'tom'], ['bob', 'tim'], ['jim', 'tim'], ['pam', 'felisa'], ['ben', 'ray'], ['james', 'ray'], ['mike', 'pam'], ['rashad', 'ben'], ['henry', 'james']]

我们使用defaultdict(标准库)中的collections来表示图形。我们在字典中使用key代表父母/管理者,并使用value代表孩子/雇员的列表:

from collections import defaultdict

graph = defaultdict(list)
for child, parent in edges:
    graph[parent].append(child)

print(graph)

输出:

defaultdict(<class 'list'>, {'john': ['jill', 'tom'], 'jill': ['tim'], 'tom': ['felisa', 'ray'], 'tim': ['bob', 'jim'], 'felisa': ['pam'], 'ray': ['ben', 'james'], 'pam': ['mike'], 'ben': ['rashad'], 'james': ['henry']})

此结构使我们可以获取带有graph[node]的节点的子级列表。我们知道,树的根是任何列表中任何值中都不存在的节点。我们还保存了较早的根。

我完全按照字面意思理解了“如何构建DiGraph以便显示以下组织结构”。这是一个示例,说明如何遍历此图结构以构建字符串表示形式:

res = ''
stack = [(root, 0)]
needed_lines = defaultdict(int)

while stack:
    node, level = stack.pop()
    prev_level = level-4
    res += '\n' + ''.join('|' if i in needed_lines else
                          ' ' if i <= level-4 else
                          '-' for i in range(level)) + node
    for child in graph[node]:
        stack.append((child, level+4))

    needed_lines[level] += len(graph[node])
    needed_lines[prev_level] -=1
    if needed_lines[prev_level] == 0: del needed_lines[prev_level]

print(res)

输出:

john
|---tom
|   |---ray
|   |   |---james
|   |   |   |---henry
|   |   |---ben
|   |       |---rashad
|   |---felisa
|       |---pam
|           |---mike
|---jill
    |---tim
        |---jim
        |---bob

答案 1 :(得分:1)

很显然,您正在尝试构建树图。如果您不知道一个节点可以有多少个直接子级,则树的最常见表示形式是 node 对象的集合。 (如果您知道每个节点的子节点数上限,并且大多数节点具有那么多子节点,则可以在一个简单数组中高效表示树。)

每个节点有1个父级和一组子级,这由某种数据容器(通常是面向对象语言的Node类的Object)表示,该数据容器包含对父级和子级的引用或指针。通常,这是父引用的单个变量,子引用的数组。没有父节点的一个节点称为 root ,并存储在一个特殊变量中,该变量用于引用整个树。

您想对树进行排列,以便轻松找到具有名称的节点。遍历各种选项可能是一门完整的计算机科学课程,因此我在这里不做介绍。实际上,您可能最终会在第二种经过排序的数据结构中存储指向节点的指针,以便于快速找到它们。

然后为每个输入找到引用的父节点,并将指定的子节点添加到该父节点。例如,在处理jill, john

  • 创建一个名为jill的新节点
  • 找到名为john的节点
  • jill添加到john的子列表中
  • jill的父级设置为john

这能回答您的问题吗?