C# 识别循环父/子记录

C# 识别循环父/子记录,c#,.net,linq,C#,.net,Linq,我试图识别并在将来防止List Objects中的父/子记录将父ID和子ID作为SampleItem类的integer属性指向它们自己。 下面是SampleItem类和SampleItem对象列表: public class SampleItem { public int ChildId{ get; set; } public int ParentId { get; set; } } List<SampleItem> i

我试图识别并在将来防止List Objects中的父/子记录将父ID和子ID作为SampleItem类的integer属性指向它们自己。 下面是SampleItem类和SampleItem对象列表:

public class SampleItem
    {
        public int ChildId{ get; set; }
        public int ParentId { get; set; }       
    }

List<SampleItem> items= new List<SampleItem>  
{
    new SampleItem{ ParentId=Null, ChildId=1 },
    new SampleItem{ ParentId=1, ChildId=2 },  
    new SampleItem{ ParentId=2, ChildId=4 },  
    new SampleItem{ ParentId=4, ChildId=1 }  
}; 

当我展开父/子关系时,它如下所示: 1->2->4->1再次->2再次->3再次…重复…1是所有的根/父

SampleItems列表对象类似于树结构,其中不允许循环引用


如何识别这些循环关系,以及如何在c代码中防止它们?如果有人能回答这个问题,我将不胜感激。

这基本上决定了链表是否有一个循环。通常的方法是以两种不同的速度递增列表,查看节点是否相等,或者递增速度较快的节点是否耗尽了列表。对基类稍加修改即可允许空值

下面的快速脏实现将确定您的集合是否有SampleItem列表的循环。这确实假设关系是一对一的,在您的示例中不是一对多的,因为这需要涵盖每个潜在孩子的情况

void Main()
{
    List<SampleItem> itemsWithCycle = new List<SampleItem>
    {
        new SampleItem{ ParentId=null, ChildId=1 },
        new SampleItem{ ParentId=1, ChildId=2 },
        new SampleItem{ ParentId=2, ChildId=4 },
        new SampleItem{ ParentId=4, ChildId=1 }
    };
    List<SampleItem> itemsWithoutCycle = new List<SampleItem>
    {
        new SampleItem{ ParentId=null, ChildId=1 },
        new SampleItem{ ParentId=1, ChildId=2 },
        new SampleItem{ ParentId=2, ChildId=4 },
        new SampleItem{ ParentId=4, ChildId=5 }
    };

    Console.WriteLine(HasCycle(itemsWithCycle)); // true
    Console.WriteLine(HasCycle(itemsWithoutCycle)); // false


    bool HasCycle(IEnumerable<SampleItem> items) {
        var slow = items.FirstOrDefault(i => i.ParentId == null);
        var fast = slow;

        while(fast != null) {
            fast = Next(Next(fast));
            slow = Next(slow);

            if(fast == slow && fast != null) return true;
        }
        return false;

        SampleItem Next(SampleItem item)
        {
            if(item == null) return null;

            return items.FirstOrDefault(i => i.ParentId == item.ChildId);
        }
    }
}

您可以使用一个简单的规则:CyrDid总是必须大于父级,您是否考虑重新构建方法,以代替跟踪整数ID,而不是跟踪对父对象和子对象SampleItem对象的引用?这并不意味着ChildId将永远大于PARTEND。ChildID可能会小于ParentID。这里我想检查父/子记录中是否存在任何循环引用。SampleItems列表对象类似于树结构,其中不允许循环引用@JSTEWARD看起来本质上是在检查链表中的循环。通常的方法是使用两个不同的指针指向起始项,然后一次递增第一个指针,一次递增第二个指针。如果更快的增量返回null,则不存在这样的循环。如果它们都是同一个节点,那么循环确实存在。@JonathonChase您能为您解释的方法添加代码示例吗?我将非常感激。我相信你的方法是解决我问题的正确方法。可以是一对多。“我的意思是,一个父母可以有很多孩子。在这种情况下,我需要做任何额外的步骤吗?”JonathonChase说
void Main()
{
    List<SampleItem> itemsWithCycle = new List<SampleItem>
    {
        new SampleItem{ ParentId=null, ChildId=1 },
        new SampleItem{ ParentId=1, ChildId=2 },
        new SampleItem{ ParentId=2, ChildId=4 },
        new SampleItem{ ParentId=4, ChildId=1 }
    };
    List<SampleItem> itemsWithoutCycle = new List<SampleItem>
    {
        new SampleItem{ ParentId=null, ChildId=1 },
        new SampleItem{ ParentId=1, ChildId=2 },
        new SampleItem{ ParentId=2, ChildId=4 },
        new SampleItem{ ParentId=4, ChildId=5 }
    };

    Console.WriteLine(HasCycle(itemsWithCycle)); // true
    Console.WriteLine(HasCycle(itemsWithoutCycle)); // false


    bool HasCycle(IEnumerable<SampleItem> items) {
        var slow = items.FirstOrDefault(i => i.ParentId == null);
        var fast = slow;

        while(fast != null) {
            fast = Next(Next(fast));
            slow = Next(slow);

            if(fast == slow && fast != null) return true;
        }
        return false;

        SampleItem Next(SampleItem item)
        {
            if(item == null) return null;

            return items.FirstOrDefault(i => i.ParentId == item.ChildId);
        }
    }
}