【问题标题】:How to check in this case if the linked list has loops or not在这种情况下如何检查链表是否有循环
【发布时间】:2019-07-02 06:35:44
【问题描述】:

必须创建一个链接列表,我们将在其中添加相互依赖或不依赖的部门。

输入将是部门名称,后跟他们所依赖的部门名称,有些可以后跟空字符串,这意味着它们不依赖于任何部门。

所以输出将首先是第二个部门,依此类推

  1. 一个部门不能靠自己(小心)

  2. 必须查找部门是否存在循环依赖(问题)

输入

销售

营销帐户

金融销售

所以输出将是

销售财务账户营销

输入

销售营销

营销帐户

帐户销售

输出

错误不能有循环依赖

尝试使用以下代码查看我的链表是否有循环

private bool DoesItHasLoops()
        {
            var fast = myLinkedList.First;
            var slow = myLinkedList.First;
            while (fast != null && fast.Next != null)
            {
                fast = fast.Next.Next;
                slow = slow.Next;
                if (slow == fast)
                    return true;


            }
            return false;
        }

它不工作,无法找到循环。

下面的代码是我在链表中​​添加字符串的方式。

private LinkedList<strings> myLinkedList;

public void AddDepartments(string[] input)
{
   if (input.Count() > 1)
      {
        if (myLinkedList.Contains(input[0]))  
         {

          myLinkedList.AddBefore(adjList.Find(input[0]), input[1]);

          }
        else
         {
          myLinkedList.AddLast(input[1]);                            
          myLinkedList.AddLast(input[0]);
         }
      }
    else
    {
     myLinkedList.AddLast(input[0]);
     myLinkedList.AddLast(string.empty());

     }
}

这适用于所有情况,只有循环情况不起作用

【问题讨论】:

  • 在您的第一个示例中,不应该是“销售财务、客户营销”吗?如果没有,我不确定我是否理解订单
  • 是的,我的错误编辑
  • 一个部门可以有多个依赖关系吗?
  • 它可能有多个依赖项
  • @Mainak Ganguly 如果您能告诉我们您最终使用了什么方法并为您的问题选择一个答案,那就太好了,因为您有两个很好的版本来解决您的问题。或者,如果您自己想出了一些东西,也可以将其发布为答案。

标签: c# .net linked-list


【解决方案1】:

我建议你稍微改变一下你的架构。而不是使用这种将所有内容都存储在LinkedList 中的构造,我会使用Dictionary。这样您就可以轻松地将部门与其所依赖的部门联系起来。

所以你会有一个Dictionary&lt;string, List&lt;string&gt;&gt;,其中的键是部门,值是它所依赖的部门:

private bool DoesItHasLoops(Dictionary<string, List<string>> departmentWithDepending)
{
    HashSet<string> checkedDepartments = new HashSet<string>();

    foreach (var department in departmentWithDepending)
    {
        checkedDepartments.Add(department.Key);
        if (HasConflict(department.Value, departmentWithDepending, checkedDepartments))
        {
            return true;
        }
        checkedDepartments.Clear();
    }
    return false;
}

private bool HasConflict(List<string> dependentDepartments,
    Dictionary<string, List<string>> departmentWithDepending,
    HashSet<string> checkedDepartments)
{
    foreach (var currentDepartment in dependentDepartments)
    {
        //Checks if this department was already checked, if yes you got a loop
        if (checkedDepartments.Contains(currentDepartment))
        {
            return true;
        }
        else
        {
            //Checks if the current department has a dependency
            if (departmentWithDepending.ContainsKey(currentDepartment))
            {
                //marks the current department as checked and and checks if the department the current department depends on has any conflicts(loops)
                checkedDepartments.Add(currentDepartment);
                if (HasConflict(departmentWithDepending[currentDepartment], departmentWithDepending, checkedDepartments))
                {
                    return true;
                }
                else
                {
                    checkedDepartments.Remove(currentDepartment);
                }
            }
        }
    }
    return false;
}

找到这些类型的循环并不容易,希望上面的方法有效,我还没有测试过。如果您有任何问题,可以在评论部分提出,因为我可以理解为什么这段代码可能难以掌握。

顺便说一句,我从内部调用 HasConflict(...) 以查找任何下一个冲突。通常我建议不要这样做,因为你会得到一个无限循环。只有当您 100% 确定它不会被卡住时,您才应该这样做。

编辑:

departments: a, b, c, d

dependencies:
a (depends on) b, c
b (depends on) (nothing)
c (depends on) d
d (depends on) a, b

dependencies that are fine:
a -> b 
a -> c -> d -> b
c -> d -> b
d -> b

dependencies that would cause a conflict:
a -> c -> d -> a
c -> d -> a -> c
d -> a -> c -> d

【讨论】:

  • 你说你会使用字典,如果一个部门依赖多个部门怎么办?您将无法拥有重复的密钥。
  • @sr28 如果您仔细阅读代码,您会发现所依赖的部门是键,而它依赖于值的部门,因此不会有任何键冲突。作者也没有提到可能存在多个依赖项。如果是这种情况,您将不得不更改代码并将 List 作为值。
  • 是的,您的代码假定没有其他部门可以依赖于同一部门。因此,例如“销售”只能有 1 个部门依赖它。听起来不切实际,因为我可以假设大多数部门都将依赖于帐户
  • 我同意 Mirko Brandt 的观点。可以存在多个依赖项。
  • 我编辑了代码,让一个部门可以有多个依赖关系。
【解决方案2】:

在我看来,识别这些循环的最简单方法是找到那些具有依赖关系的部门在某个时候也被列为依赖关系本身的那些,并遍历它们之间的依赖关系链以查看它们是否连接。也就是说,你的架构似乎有点不对劲。我会创建一个对象来保存部门及其依赖项列表以及是否具有这样的循环引用的标志:

public class Department
{
    public int Id { get; set; }
    public string Name { get; set; }
    public IList<int> Dependencies { get; set; }
    public bool HasCircularReferences { get; set; } 
}

然后,您传入一个部门列表并遍历每个部门,并找到将该部门列为依赖项的其他部门。然后,您通过依赖链从这些方面进行工作,以查看您是否最终回到当前部门。您可以使用这样的循环(注意这是一个静态方法,因为我在控制台应用程序中对其进行了测试):

private static void GetDependents(IList<int> dependencies, int seed)
{
    foreach (var dependent in dependencies)
    {
        var dependentDept = departments.Where(d => d.Id == dependent).FirstOrDefault();
        if (dependentDept.Dependencies.Count > 0)
        {
            if (dependencies.Contains(seed) || dependentDept.HasCircularReferences)
            {
                SetCircularReferenceFlag(seed, true);
                break;
            }
            GetDependents(dependentDept.Dependencies, seed);
        }
        else
        {
            SetCircularReferenceFlag(seed, false);
            break;
        }
    }
}

这使用 SetCircularReferenceFlag 方法在部门上设置 HasCircularReferences 标志,即:

private static void SetCircularReferenceFlag(int departmentId, bool hasCircularReferences)
{
    var seedDept = departments.Where(d => d.Id == departmentId).FirstOrDefault();
    seedDept.HasCircularReferences = hasCircularReferences;
}

然后可以为列表中的每个部门调用 GetDependents 方法,如下所示:

foreach (var department in departments)
{
    GetDependents(department.Dependencies, department.Id);
}

已使用上面定义的部门对象将部门设置为如下所示:

private static List<Department> departments = new List<Department>
{
    new Department
    {
        Id = 1,
        Name = "sales",
        Dependencies = new List<int>{ 2 }
    },
    new Department
    {
        Id = 2,
        Name = "accounts",
        Dependencies = new List<int>{ 3 }
    },
    new Department
    {
        Id = 3,
        Name = "marketing",
        Dependencies = new List<int>{ 1 }
     },
     new Department
     {
        Id = 4,
        Name = "finance",
        Dependencies = new List<int>{ 1 }
     }
 };

通过控制台应用程序运行时,您会得到以下结果:

【讨论】:

  • if (dependencies.Contains(seed) || dependentDept.HasCircularReferences)这部分我没看懂,你为什么要遍历依赖项,每次都检查依赖项是否包含种子?
  • 种子是指您正在查看原始部门列表的部门。如果一个依赖包含这个,那么它必须有一个循环引用。同样,如果一个部门(种子)依赖于一个部门,而该部门本身有一个循环引用,那么当前部门(种子)也必须有一个循环引用
  • 我理解你的思考过程,我只是不明白你为什么在循环内做if(dependencies.Contains(seed)),因为种子和依赖项都不会在循环内改变。在循环之外检查它不是更有意义吗?
  • 我可以将 dependencies.Contains(seed) 排除在循环之外,但我正在使用dependentDept.Dependencies.Count > 0 检查,以确保我不会这样做,除非我需要。如果我这样做了,我还需要另一个调用来在我目前拥有的 2 之上设置标志
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-19
  • 2012-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多