问题就像这样
建议数据结构并编写程序以计算员工(直接或间接)在线性时间内转介的员工数量。例如
A B C D E F G
A 0 1 0 0 0 0 0 A referred 4 (A referred B, B referred C and D and D referred E)
B 0 0 1 1 0 0 0 B referred 3
C 0 0 0 0 0 0 0
D 0 0 0 0 1 0 0 D referred 1
E 0 0 0 0 0 0 0
F 0 0 0 0 0 0 1 F referred 1
G 0 0 0 0 0 0 0
使用二维数组做到了,可以在线性时间内完成吗?
请注意,员工显然不会被他直接或间接推荐的人推荐,因此图表中没有周期。只有一名员工可以推荐新员工。而每位员工都可以推荐多名员工。
答案 0 :(得分:2)
我使用C#的解决方案,我很确定这比N ^ 2少,但我需要再看一下它。在我这样做时发表评论。
public class Employee
{
public List<Employee> Referred { get; set; }
public string Name { get; set; }
public int CountOfRefer { get; set; }
public bool BeenCounted { get; set; }
public Employee()
{
Referred = new List<Employee>();
}
}
public static void main()
{
Employee A = new Employee(){ Name="A" };
Employee B = new Employee(){ Name="B" };
Employee C = new Employee(){ Name="C" };
Employee D = new Employee(){ Name="D" };
Employee E = new Employee(){ Name="E" };
Employee F = new Employee(){ Name="F" };
Employee G = new Employee(){ Name="G" };
A.Referred.Add(B);
B.Referred.Add(C);
B.Referred.Add(D);
D.Referred.Add(E);
F.Referred.Add(G);
List<Employee> employees = new List<Employee>()
{
A, B, C, D, E, F, G
};
foreach (var emp in employees)
{
if (!emp.BeenCounted) countRefers(emp);
}
}
private static int countRefers(Employee emp)
{
if (!emp.BeenCounted && emp.Referred.Count != 0)
{
foreach (var refs in emp.Referred)
{
emp.CountOfRefer += countRefers(refs);
}
}
emp.BeenCounted = true;
emp.CountOfRefer += emp.Referred.Count;
return emp.CountOfRefer;
}
基本上,当计算一名员工时,它会向树上递减,直到找到一个没有引用的人(应该保证在那里,因为人们不能互相推荐(我猜除非只有一个人,否则忽略现在的情况))。然后,如果必须通过递归计算任何人,它会设置一个标志,因此不需要再次计算它。
答案 1 :(得分:0)
图表方法:首先构建有向图(在这种情况下为树),其中每个节点代表一个雇员,并指向引用它们的雇员的节点。这应该是O(N ^ 2)最坏情况((N ^ 2)/ 2检查是准确的)其中N是雇员的数量。在构建此图时,您应该跟踪此树的叶子(未引用任何人的员工),然后修剪这些节点(为其引用数量指定零)并将引用数量加1推荐他们的员工。然后重复使用新叶子,除了为每个引用节点添加2个引用数量。整个修剪和计数过程也应该采用O(N),最后所有节点都包含每个员工的推荐数量。
这假设图表中没有周期或奇怪的两个雇员 - 指的是同一个雇员的情况(即它实际上是一棵树)。
因此,如果问题的输入数据是您描述的二进制向量,则不能线性完成。如果我们只给出了每个节点的雇佣员工的姓名,那么O(N)就有可能。
答案 2 :(得分:0)
您可以先构建图表。这不能包括在O(n)中,因为它显然是O(n ^ 2)
目前尚不清楚是否优化了重复引用(想象一下所有的值都是1)
此时,您可以从一个节点开始并添加所有引用的节点,例如有点面具。添加的任何值都需要递归导航,直到您不添加新节点。
您访问的节点数量为O(N),因为每个员工都可以推荐一个人。
答案 3 :(得分:0)
Java 如果允许作弊:枚举可能代表图表。 先写A(B),B(C,D),然后重新排序,这样就没有编译器抱怨的前向引用。 (对于非循环引用,这始终是可能的。即使是针对循环引用的编译时检查。)
public class Refers {
enum RefEnum {
C(),
E(),
G(),
D(E),
F(G),
B(C, D),
A(B);
//private final RefEnum[] refs;
public final int refCount;
private RefEnum(final RefEnum... refs) {
//this.refs = refs;
int n = 0;
for (RefEnum ref : refs) {
n += 1 + ref.refCount;
}
refCount = n;
}
}
public static void main(String[] args) {
for (RefEnum ref : RefEnum.values()) {
System.out.printf("%s has %d refers%n",
ref.toString(), ref.refCount);
}
}
}
然而,该算法保持为O(N²)。
答案 4 :(得分:0)
这是一棵树(更确切地说是一片森林)。在O(n)时间读取边缘。在O(n.log(n))中构造树。给定一个节点,访问并计算O(n)中的后代。
答案 5 :(得分:0)
您可以维护所有员工的邻接列表(N空间)。然后,对于每个员工,为所有被推荐的员工维护一个类似数据结构的包。然后,为了计算员工A的参考,您可以从A开始运行DFS(深度优先搜索),这是线性时间算法。
Java代码在这里 -
http://yogeshsp.blogspot.com/2013/04/graph-api.html
http://yogeshsp.blogspot.com/2013/05/depth-first-search-dfs-graph-processing.html