如果随机访问不可用,如何在每个(无序)的不同集合元素中获得性能

时间:2016-07-30 12:56:16

标签: c# iteration ienumerator

示例:我有集合{1, 2, 3, 4}。 我想得到所有(无序)对不同的元素,它们是: {1,2}{1,3}{1,4}{2,3}{2,4}{3,4}

如果我有IList,我可以这样做:

IList<MyType> list = ... // fill the list elements

for (int i = 0; i < list.Count - 1; i++)
    for (int j = i+1; j < list.Count; j++)
        {
            ... // now we have the pair of list[i] and list[j]
        }

对于LinkedList我也知道怎么做;除了索引ij之外,我们有两个LinkedListNode s fstsnd,它几​​乎相同。每当我们致电fst.Next时,我们都会设置snd = fst.Next

对于n元素的列表,上述方法需要(n-1)*n/2个迭代步骤。 (对于i = 0,我们有j = 1 ... n-1,所以n-1步。对于i = 1,我们有j = 2 ... n-1,所以n-2步。等等。总计最多(n-1) + (n-2) + ... + 3 + 2 + 1 = (n-1)*n/2步。)

对于任何ICollection,有没有办法做到这一点?我认为IEnumerator可以做到这一点,但似乎没有办法告诉IEnumerator“移动到那个参考!”就像我可以用LinkedListNode s。

编辑:

我不是在寻找这个解决方案:

foreach (MyType a in list)
    foreach (MyType b in list)
    {
        if (a == b)
            continue;

        ... // now we have the pair of a and b
    }

对于n个元素的集合,此方法需要n^2个迭代步骤,这比上述方法略长一倍,因此显然效果不佳。

编辑:

事先将集合转换为List似乎是最简单的方法,对于大型n,性能损失可以忽略不计,因为它只会为整个交互添加n个步骤(新列表必须由n元素填充。所以我会坚持下去。

2 个答案:

答案 0 :(得分:2)

编辑:根据评论中的讨论和编辑过的问题,似乎在列表中复制可枚举是最好的行动方案。

旧答案:

在我看来,您需要将项目放入某种哈希表数据结构中。

这是一个使用GroupBy的基于LINQ的解决方案

var items = new List<int> { 1, 2, 3, 2, 3 };

var groupedItems = from i in items
                   group i by i into g
                   select g;

foreach (var group in groupedItems)
{
    //group.Key stores the key of the grouping
    foreach (var item in group) //group contains items with the same key
    {
        //Do something
        Console.WriteLine(item);
    }
}

此处每个组都包含具有相同键的项(在此示例中,因为项是int,项是键,但您可以按所需的任何表达式进行分组)

或者,您可以使用字典自行分组内容

var items = new List<int> { 1, 2, 3, 2, 3 };
var itemsDictionary = new Dictionary<int, List<string>>();

foreach (int i in items)
{
    List<string> repeatedValues;
    if(itemsDictionary.TryGetValue(i, out repeatedValues))
    {
        repeatedValues.Add(i.ToString());
    }
    else
    {
        itemsDictionary.Add(i, new List<string> { i.ToString() });
    }
}

foreach (KeyValuePair<int, List<string>> kvp in itemsDictionary)
{
    //do whatever is needed kvp.Value

}

在这个例子中,我使用了一个字符串来模拟int的键和不同类型的值。我认为这两个解决方案都是NlogN

或者 - 在列表中对集合进行排序 - 所有相等的元素将一个接一个地放置。这将是NlogN解决方案。

答案 1 :(得分:2)

IEnumerable是一个仅向前的迭代器; ICollection没有索引访问权限。您可以做的是在第一次迭代期间将可枚举放入缓冲区,然后使用嵌套的for循环。

var enumerable = Enumerable.Range(0, 10);

var buffer = new List<int>();

using (var enumerator = enumerable.GetEnumerator())
{
    if (enumerator.MoveNext())
    {
        buffer.Add(enumerator.Current);
        while (enumerator.MoveNext())
        {
            var current = enumerator.Current;
            buffer.Add(current);

            Handle(buffer[0], current);
        }
    }
}

for (int i = 1; i < buffer.Count - 1; i++)
    for (int j = i + 1; j < buffer.Count; j++)
        Handle(buffer[i], buffer[j]);

或者,如果您不再关心再次浏览这些项目,可以使用enumerable.ToArray(),然后在该数组上使用嵌套的for