【问题标题】:C# Similarities of two arraysC# 两个数组的相似之处
【发布时间】:2012-02-28 19:49:28
【问题描述】:

我确信必须有更好的方法来做到这一点......

// Simplified code
var a = new List<int>() { 1, 2, 3, 4, 5, 6 };
var b = new List<int>() { 2, 3, 5, 7, 11 };
var z = new List<int>();
for (int i = 0; i < a.Count; i++)
    if (b.Contains(a[i]))
        z.Add(a[i]);
// (z) contains all of the numbers that are in BOTH (a) and (b), i.e. { 2, 3, 5 }

我不介意使用上述技术,但我想要一些快速有效的方法(我需要多次比较非常大的列表),而这似乎两者都不是!有什么想法吗?

编辑:因为它有所不同 - 我使用的是 .NET 4.0,初始数组已经排序并且不包含重复项。

【问题讨论】:

标签: c# arrays


【解决方案1】:

您可以使用 IEnumerable.Intersect。

var z = a.Intersect(b);

这将可能比您当前的解决方案更有效。

请注意,您遗漏了一项重要信息 - 列表是否已排序。如果它们是几个嵌套循环,每个输入数组只传递一次,每个循环可能会更快 - 并且编写起来更有趣。

编辑 针对您对订购的评论:

第一次尝试循环 - 它需要代表您进行一些调整,但适用于您的初始数据。

    int j = 0;
    foreach (var i in a)
    {
        int x = b[j];
        while (x < i)
        {
            if (x == i)
            {
                z.Add(b[j]);
            }
            j++;
            x = b[j];
        }
    }

这是您需要添加一些单元测试的地方;)

编辑 最后一点 - Linq 很可能可以使用 SortedList 非常有效地执行这个交集,如果性能是一个问题,那么值得测试各种解决方案。如果您以无序方式加载数据,请不要忘记考虑排序。

一个最终编辑,因为在这方面有一些反复,人们可能会在没有正确调试的情况下使用上面的内容,所以我在此处发布更高版本:

        int j = 0;
        int b1 = b[j];
        foreach (var a1 in a)
        {
            while (b1 <= a1)
            {
                if (b1 == a1)
                    z1.Add(b[j]);
                j++;
                if (j >= b.Count)
                    break;
                b1 = b[j];
            }
        }

【讨论】:

  • 初始数组是有序的
  • 感谢您的更新,并感谢 Stack over-flow 解决了问题,所以我无法发布我的发现...叹息。
  • 谎言和诽谤!对不起骰子,但我没有在你的代码之前重置秒表,你的代码是迄今为止最有效的!我重置秒表后花了 15 个滴答声。太棒了。
  • Intersect() 似乎有点奇怪 - “加载”大约需要 2,750 个滴答声,然后无论您在第二次/第三次/后续调用中向其抛出什么数据集,都需要大约 1 个滴答声才能完成.您的方法在后续调用中花费的时间更长(与 Intersect 相比),但我必须进行大量数组比较才能使使用 Intersect 成为更好的选择。 (使用我的测试数据,我必须进行超过 250 次比较 - 在​​我的场景中不太可能 - 你的方法总体上会变慢)
  • 另外,我可以通过使用 int[] 而不是 List 将您的方法花费的时间减半
【解决方案2】:

有IEnumerable.Intersect,但由于这是一种扩展方法,我怀疑它是否会非常有效。

如果你想要效率,把一个列表变成一个集合,然后遍历第二个列表,看看集合中有哪些元素。请注意,我预先分配了z,只是为了确保您不会受到任何重新分配的影响。

var set = new HashSet<int>(a);
var z = new List<int>(Math.Min(set.Count, b.Count));

foreach(int i in b)
{
    if(set.Contains(i))
        a.Add(i);
}

这保证在 O(N+M) 中运行(N 和 M 是两个列表的大小)。

现在,您可以使用 set.IntersectWith(b),我相信它会同样有效,但我不能 100% 确定。

【讨论】:

  • 我并不是说扩展方法本身效率低下,我只是说它只知道 IEnumerable 接口,因此很难高效(除非他们在内部使用 Set ,是吗? )
【解决方案3】:

Intersect() 方法就是这样做的。来自MSDN

使用默认值生成两个序列的集合交集 相等比较器来比较值。

所以在你的情况下:

var z = a.Intersect(b);

【讨论】:

  • 谢谢,如果您知道要在 Google 中输入哪个词,那就容易多了!
【解决方案4】:

System.Collections.Generic 命名空间中使用SortedSet&lt;T&gt;

SortedSet<int> a = new SortedSet<int>() { 1, 2, 3, 4, 5, 6 };
SortedSet<int> b = new SortedSet<int>() { 2, 3, 5, 7, 11 };
b.IntersectWith(s2);

但你肯定没有重复!
尽管您的第二个列表不必是SortedSet。它可以是任何集合 (IEnumerable&lt;T&gt;),但在内部该方法的行为方式是,如果第二个列表也是 SortedSet&lt;T&gt;,则该操作是 O(n) 操作。

【讨论】:

  • 为什么要使用排序集?为什么不只是一个集合?
  • @zmbq:.NET Framework 中没有 Set 类(只有 System.Linq.Expressions 中的内部类)。而 SortedSet 是必须高效的。它在内部使用平衡二叉树。
  • @MD.Unicorn:有一个 HashSet 用于此目的。
【解决方案5】:

如果您可以使用 LINQ,则可以使用 Enumerable.Intersect() 扩展方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-17
    • 2011-02-01
    • 1970-01-01
    • 2023-03-11
    • 2019-02-01
    • 1970-01-01
    相关资源
    最近更新 更多