可以在线性时间内解决,在n ^ 2时间内完成

时间:2013-07-31 19:58:07

标签: algorithm language-agnostic performance

问题就像这样

建议数据结构并编写程序以计算员工(直接或间接)在线性时间内转介的员工数量。例如

  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 

使用二维数组做到了,可以在线性时间内完成吗?

请注意,员工显然不会被他直接或间接推荐的人推荐,因此图表中没有周期。只有一名员工可以推荐新员工。而每位员工都可以推荐多名员工。

6 个答案:

答案 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