Arrays 算法:基于约束对数组中的对象排序

Arrays 算法:基于约束对数组中的对象排序,arrays,algorithm,sorting,Arrays,Algorithm,Sorting,我有一千个类型为MyClass class MyClass{ array<MyClass> objectsBehind; Boolean large; } 在第一个序列中,我将三个大对象分组在一起,而不是在第二个示例中分散 我想不出一个好办法。有什么想法吗?这很容易做到 首先,要在每个此类对象的objectsBehind数组中的所有对象之前对对象进行排序,可以使用 拓扑排序将在每次主迭代中“同时”将多个元素放入输出集合 您可以按“大”属性对这些对象进行排序,以便所

我有一千个类型为
MyClass

class MyClass{
    array<MyClass> objectsBehind;
    Boolean large;
}

在第一个序列中,我将三个大对象分组在一起,而不是在第二个示例中分散


我想不出一个好办法。有什么想法吗?

这很容易做到

首先,要在每个此类对象的
objectsBehind
数组中的所有对象之前对对象进行排序,可以使用

拓扑排序将在每次主迭代中“同时”将多个元素放入输出集合

您可以按“大”属性对这些对象进行排序,以便所有大对象排在第一位,然后是所有非大对象。您可能希望在此添加另一个排序条件,以便可以依赖大型对象内部和非大型对象内部的已知顺序

基本上,以下是拓扑排序的工作原理(我所学的):

  • 首先创建两个数据结构来保存“图形”,即对象之间的所有链接:
  • 您需要一个包含每个对象的字典,以及一个包含它链接到的所有对象的列表(在您的情况下,实际上不需要这样做,因为每个对象都在
    objectsBehind
    属性中内置了此项)
  • 您需要一个包含链接到的每个对象以及指向该对象的链接数量的字典
  • 您将需要一个包含所有对象的哈希集,这些对象没有链接(目前)
  • 相应地填充这些数据结构
  • 将所有对象放在哈希集中(就好像它们根本没有入站链接一样)
  • 循环遍历所有具有链接的对象,我们将此对象称为迭代
  • 添加对象(具有来自该对象的链接)及其到第一个字典的所有链接(同样,您的对象不需要这样做)
  • 通过增加对象中每个链接的入站链接数来调整第二个字典
  • 当您增加一个对象的入站链接数时,请从哈希集中删除同一个对象(我们现在知道它至少有一个入站链接)
  • 开始一个循环,基本上说“只要hashset中有东西”,这将查看hashset,它现在包含所有没有入站链接的对象
  • 在每个循环迭代中,首先输出hashset中的所有对象。这是剩下的东西中的“第一个”。您希望订购这些产品以产生稳定的排序。在您的情况下,我会先订购所有大型对象,然后订购所有非大型对象
  • 为了枚举的目的,制作一个hashset的副本,并清除hashset,为下一个循环迭代做准备
  • 循环遍历副本中的所有对象,对于每个对象,循环遍历来自它的所有出站链接,对于每个链接,在第二个字典中减少目标上的入站链接数
  • 当这样一个数字(指向对象的入站链接的数量)在减少后达到零时,这意味着不再有任何指向它的“活动”链接,因此将其添加到哈希集中
  • 循环(点4及以上)
  • 如果在第8点和第4点之后,hashset中没有对象,但第二个字典中有未达到零入站链接的对象,则表示图形中有循环,即“对象1指向对象2,对象2指向3,3指向1”

    这里有一个这样的解决方案,您可以在中进行测试

    请注意,有许多方法可以进行拓扑排序。例如,此版本将获取前面没有对象的所有对象,并同时输出这些对象。但是,您可以对直接位于其中一些对象之后的对象进行分组,直接位于它们后面的对象之后,并且仍然不违反任何约束

    例如,检查下面代码中4和7之间的关系(您必须运行它)


    我有点困惑。在您的示例中,看起来您可以通过
    large
    值将索引任意分配给要分组的值。如果情况并非如此,您能否举例说明如何根据
    large
    对已排序的数据进行分组?创建两个集合,一个是
    large=true
    并对其排序,另一个是
    large=false
    并对其排序。否则,任何两个
    large=true
    元素相邻的事实只是第一个排序条件的属性。我想不出一个有意义的方法来包含
    large=true
    元素,让它们按顺序排列,并且不违反您的第一个标准。
    1000
    对于许多平台来说是一个非常小的数字,以至于一个有序的序列在任何情况下都可能是不相关的。但标准库为您提供了模板化容器,您甚至可以在其中使用自己的谓词并重载给定的运算符,以根据给定的字段保持数据结构有序。快速查看wikipedia条目表明,拓扑排序确实可能正是我所需要的。我会试一试,稍后再来。非常感谢。太神了非常感谢你的努力。非常好的回答让我知道(在这里或lasse@vkarlsen.no)如果您对代码或方法有疑问。
    array[45].large = false; array[46].large = true, array[47].large = true, array[48].large = true, array[49].large = false;
    
    array[45].large = true; array[46].large = false, array[47].large = true, array[48].large = false, array[49].large = true; 
    
    const int OBJECT_NUM = 10;
    
    void Main()
    {
        Random r = new Random(12345);
        var objects = new List<MyClass>();
        for (int index = 1; index <= OBJECT_NUM; index++)
        {
            var mc = new MyClass { Id = index, IsLarge = (r.Next(100) < 50) };
            objects.Add(mc);
        }
        for (int index = 0; index < objects.Count; index++)
        {
            var temp = new List<MyClass>();
            for (int index2 = index + 1; index2 < objects.Count; index2++)
                if (r.Next(100) < 10 && index2 != index)
                    temp.Add(objects[index2]);
            objects[index].ObjectsBehind = temp.ToArray();
        }
    
        objects.Select(o => o.ToString()).Dump("unsorted");
        objects = LargeTopoSort(objects).ToList();
        objects.Select(o => o.ToString()).Dump("sorted");
    }
    
    public static IEnumerable<MyClass> LargeTopoSort(IEnumerable<MyClass> input)
    {
        var inboundLinkCount = new Dictionary<MyClass, int>();
        var inputArray = input.ToArray();
    
        // the hash set initially contains all the objects
        // after the first loop here, it will only contain objects
        // that has no inbound links, they basically have no objects
        // that comes before them, so they are "first"
        var objectsWithNoInboundLinks = new HashSet<MyClass>(inputArray);
    
        foreach (var source in inputArray)
        {
            int existingInboundLinkCount;
    
            foreach (var target in source.ObjectsBehind)
            {
                // now increase the number of inbound links for each target
                if (!inboundLinkCount.TryGetValue(target, out existingInboundLinkCount))
                    existingInboundLinkCount = 0;
                existingInboundLinkCount += 1;
                inboundLinkCount[target] = existingInboundLinkCount;
    
                // and remove it from the hash set since it now has at least 1 inbound link
                objectsWithNoInboundLinks.Remove(target);
            }
        }
    
        while (objectsWithNoInboundLinks.Count > 0)
        {
            // all the objects in the hash set can now be dumped to the output
            // collection "at the same time", but let's order them first
    
            var orderedObjects =
                (from mc in objectsWithNoInboundLinks
                 orderby mc.IsLarge descending, mc.Id
                 select mc).ToArray();
    
            foreach (var mc in orderedObjects)
                yield return mc;
    
            // prepare for next "block" by clearing the hash set
            // and removing all links from the objects we just output
            objectsWithNoInboundLinks.Clear();
    
            foreach (var source in orderedObjects)
            {
                foreach (var target in source.ObjectsBehind)
                {
                    if (--inboundLinkCount[target] == 0)
                    {
                        // we removed the last inbound link to an object
                        // so add it to the hash set so that we can output it
                        objectsWithNoInboundLinks.Add(target);
                    }
                }
            }
        }
    }
    
    public class MyClass
    {
        public int Id; // for debugging in this example
        public MyClass[] ObjectsBehind;
        public bool IsLarge;
    
        public override string ToString()
        {
            return string.Format("{0} [{1}] -> {2}", Id, IsLarge ? "LARGE" : "small", string.Join(", ", ObjectsBehind.Select(o => o.Id)));
        }
    }
    
    unsorted 
    1 [LARGE] -> 5 
    2 [LARGE] ->  
    3 [small] ->  
    4 [small] -> 7 
    5 [small] ->  
    6 [small] ->  
    7 [LARGE] ->  
    8 [small] ->  
    9 [LARGE] -> 10 
    10 [small] ->  
    
    
    sorted 
    1 [LARGE] -> 5 
    2 [LARGE] ->  
    9 [LARGE] -> 10 
    3 [small] ->  
    4 [small] -> 7 
    6 [small] ->  
    8 [small] ->  
    7 [LARGE] ->  
    5 [small] ->  
    10 [small] ->