首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >基于约束的数组对象排序算法

基于约束的数组对象排序算法
EN

Stack Overflow用户
提问于 2013-08-01 17:01:27
回答 1查看 472关注 0票数 2

我有1000个MyClass类型的对象

代码语言:javascript
复制
class MyClass{
    array<MyClass> objectsBehind;
    Boolean large;
}

其中objectsBehindMyClass对象的数组,该数组中的任何对象都是1000个原始对象的一部分。

我将它们放置在数组中,并对它们进行排序,使对象在数组中的索引高于其objectsBehind数组中的对象。例如,索引546处的对象不能在排序数组中索引高于546的objectsBehind数组中包含任何对象。

我的问题是这个。在这1000个对象中,大约有20个具有属性large = true。如果不违反objectsBehind属性,则在排序数组中按顺序对这些“大型”对象进行分组是有益的。例如,最好这样做:

代码语言:javascript
复制
array[45].large = false; array[46].large = true, array[47].large = true, array[48].large = true, array[49].large = false;

代码语言:javascript
复制
array[45].large = true; array[46].large = false, array[47].large = true, array[48].large = false, array[49].large = true; 

在第一个序列中,我将三个大型对象组合在一起,而不是在第二个示例中展开它们。

我想不出一个好办法来做这件事。有什么想法吗?

EN

Stack Overflow用户

回答已采纳

发布于 2013-08-01 17:16:32

这可能很容易做到。

首先,要对每个这样的对象的objectsBehind数组中的所有对象进行排序,可以使用拓扑排序

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

这些对象按大属性进行排序,这样所有的大型对象都是第一位的,然后是所有非大型对象。您可能希望在这里添加另一个排序条件,这样您就可以依赖于大型对象和非大型对象中已知的顺序。

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

  1. 首先创建几个数据结构来保存“图”,即。对象之间的所有链接:
    1. 您将需要一个包含每个对象的字典,以及一个包含其链接的所有对象的列表(在您的情况下,这实际上是不需要的,因为每个对象都内置在objectsBehind属性中)
    2. 您将需要一个字典,其中包含链接到的每个对象,以及这些链接指向对象的数量
    3. 您将需要一个没有链接到它们的所有对象的哈希集(目前)

  1. 相应地填充这些数据结构( )
    1. 将所有对象放置在哈希集中(就好像它们根本没有入站链接一样)
    2. 循环遍历所有有链接的对象,我们将调用这个对象迭代A
    3. 添加对象(其中有来自它的链接)和它与第一个字典的所有链接(同样,这对于您的对象不需要)
    4. 通过增加来自A对象的每个链接的入站链接数来调整第二个字典
    5. 当增加对象的入站链接数时,从哈希集中删除相同的对象(我们现在知道它至少有一个入站链接)。

  1. 启动一个循环,该循环基本上是“只要我在哈希集中有一些内容”,这将查看哈希集,它现在包含所有没有入站链接的对象。
  2. 在每个循环迭代中,首先输出哈希集中的所有对象。这是剩下的“第一”。你想要订购这些来产生一个稳定的种类。在您的例子中,我将首先排序所有大型对象,然后是所有非大型对象。
  3. 为枚举目的,制作hashset的副本,并清除hashset,为下一个循环迭代做准备。
  4. 循环遍历副本中的所有对象,对于每个对象,循环遍历来自它的所有出站链接,对于每个链接,在第二个字典中减少目标上的入站链接的数量。
  5. 当这样一个数字,即对象的入站链接数在减少后达到零,这意味着不再有任何指向对象的“活动”链接,因此将其添加到哈希集。
  6. 绕圈(第4点及以后)

如果在第8和第4点之后,您在哈希集中没有对象,但是第二个字典中的对象没有达到零入站链接,这意味着您在图中有循环,即。“对象1指向对象2,对象2指向3,以及3指向1”。

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

请注意,有很多方法来做拓扑排序。例如,这里的这个版本将接受所有没有对象的对象,并同时输出这些对象。但是,您可能会将直接在其中一些之后出现的对象分组,在它们之后直接对对象进行分组,并且仍然不会违反任何约束。

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

代码语言:javascript
复制
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)));
    }
}

输出:

代码语言:javascript
复制
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] ->  
票数 5
EN
查看全部 1 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18000211

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档