C# 从两个列表(或数组)中匹配项
我在工作中遇到了一个问题,希望可以归结为以下几点:我有两个C# 从两个列表(或数组)中匹配项,c#,.net,arrays,list,C#,.net,Arrays,List,我在工作中遇到了一个问题,希望可以归结为以下几点:我有两个Lists,我想看看ListA中的ints是否等于ListB中的int。(它们可以是数组,如果这样可以简化操作,但我认为List有一些内置的魔法可能会有所帮助。)我确信这是一个LINQ友好的问题,但我在这里使用的是2.0 到目前为止,我的解决方案是通过ListA使用foreach,然后通过ListB使用foreach foreach (int a in ListA) { foreach (int b in ListB) {
List
s,我想看看ListA
中的int
s是否等于ListB
中的int
。(它们可以是数组,如果这样可以简化操作,但我认为List
有一些内置的魔法可能会有所帮助。)我确信这是一个LINQ友好的问题,但我在这里使用的是2.0
到目前为止,我的解决方案是通过ListA使用foreach
,然后通过ListB使用foreach
foreach (int a in ListA)
{
foreach (int b in ListB)
{
if (a == b)
{
return true;
}
}
}
当它们都有三个项目长时,这实际上是非常巧妙的,但是现在它们有200个项目长,并且经常不匹配,所以我们得到了N^2比较的最坏情况。即使是40000个比较也进行得相当快,但我认为我可能遗漏了一些东西,因为对于这个特定的问题,N^2似乎非常幼稚
谢谢 使用BinarySearch方法而不是迭代内部循环中的所有元素如何?将整个ListA加载到一个HashSet实例中,然后针对HastSet测试ListB中的每个项:我非常确定这将是O(N)
//前面有未测试的代码
HashSet HashSet=新的HashSet(ListA);
foreach(列表B中的int i)
{
if(hashSet.Contains(i))
返回true;
}
在一行中有同样的内容:
return new HashSet<int>(ListA).Overlaps(ListB);
返回新的HashSet(ListA)。重叠(ListB);
HashSet在.NET 3.5中不存在,因此在.NET 2.0中,您可以使用
字典(而不是使用HashSet
),并且始终将null作为对象/值存储在字典中,因为您只对键感兴趣。与其遍历每个列表,不如看看该方法:
Chris通过散列给出了一个O(N)解。现在,根据常量因子(由于散列),可能需要考虑通过排序的O(N log(N))解决方案。根据您的用例,有几个不同的变体可以考虑。
排序ListB(O(N log(N)),并使用搜索算法解析ListA中的每个元素(同样是O(N)*O(log(N)))
对ListA和ListB进行排序(O(N log(N)),并使用O(N)算法来比较这些列表中的重复项
如果两个列表将被多次使用,则首选第二种方法。对于,这很简单,因为您可以在上调用,以提供两个数组的集合交集:
var intersection = ListA.Intersect(ListB);
但是,这是设置的交叉点,这意味着如果ListA
和ListB
中没有唯一的值,则不会获得任何副本。换句话说,如果您具有以下内容:
var ListA = new [] { 0, 0, 1, 2, 3 };
var ListB = new [] { 0, 0, 0, 2 };
然后ListA.Intersect(ListB)
生成:
{ 0, 2 }
如果您正在期待:
{ 0, 0, 2 }
然后,在扫描这两个列表时,您必须自己维护项目计数和产量/减量
首先,您需要收集一个包含单个项目列表的列表:
var countsOfA = ListA.GroupBy(i => i).ToDictionary(g => g.Key, g => g.Count());
从那里,您可以扫描ListB
,并在countsOfA
中遇到项目时将其放入列表中:
// The items that match.
IList<int> matched = new List<int>();
// Scan
foreach (int b in ListB)
{
// The count.
int count;
// If the item is found in a.
if (countsOfA.TryGetValue(b, out count))
{
// This is positive.
Debug.Assert(count > 0);
// Add the item to the list.
matched.Add(b);
// Decrement the count. If
// 0, remove.
if (--count == 0) countsOfA.Remove(b);
}
}
//匹配的项目。
IList matched=新列表();
//扫描
foreach(列表b中的int b)
{
//伯爵。
整数计数;
//如果在a中找到该项。
if(countsOfA.TryGetValue(b,out count))
{
//这是积极的。
断言(计数>0);
//将项目添加到列表中。
(b)加入(b);
//减少计数。如果
//0,删除。
如果(--count==0)countsOfA.移除(b);
}
}
您可以将其封装在一个延迟执行的扩展方法中,如下所示:
public static IEnumerable<T> MultisetIntersect(this IEnumerable<T> first,
IEnumerable<T> second)
{
// Call the overload with the default comparer.
return first.MultisetIntersect(second, EqualityComparer<T>.Default);
}
public static IEnumerable<T> MultisetIntersect(this IEnumerable<T> first,
IEnumerable<T> second, IEqualityComparer<T> comparer)
{
// Validate parameters. Do this separately so check
// is performed immediately, and not when execution
// takes place.
if (first == null) throw new ArgumentNullException("first");
if (second == null) throw new ArgumentNullException("second");
if (comparer == null) throw new ArgumentNullException("comparer");
// Defer execution on the internal
// instance.
return first.MultisetIntersectImplementation(second, comparer);
}
private static IEnumerable<T> MultisetIntersectImplementation(
this IEnumerable<T> first, IEnumerable<T> second,
IEqualityComparer<T> comparer)
{
// Validate parameters.
Debug.Assert(first != null);
Debug.Assert(second != null);
Debug.Assert(comparer != null);
// Get the dictionary of the first.
IDictionary<T, long> counts = first.GroupBy(t => t, comparer).
ToDictionary(g => g.Key, g.LongCount(), comparer);
// Scan
foreach (T t in second)
{
// The count.
long count;
// If the item is found in a.
if (counts.TryGetValue(t, out count))
{
// This is positive.
Debug.Assert(count > 0);
// Yield the item.
yield return t;
// Decrement the count. If
// 0, remove.
if (--count == 0) counts.Remove(t);
}
}
}
公共静态IEnumerable MultisetIntersect(此IEnumerable首先,
(可数秒)
{
//使用默认比较器调用重载。
返回first.MultisetIntersect(第二个,EqualityComparer.Default);
}
公共静态IEnumerable MultiSetterSect(此IEnumerable优先,
IEnumerable秒,IEqualityComparer比较器)
{
//验证参数。单独执行此操作,以便检查
//立即执行,而不是在执行时执行
//发生了。
如果(first==null)抛出新的ArgumentNullException(“first”);
如果(second==null)抛出新的ArgumentNullException(“second”);
如果(comparer==null)抛出新的ArgumentNullException(“comparer”);
//推迟执行内部命令
//例如。
返回第一个.MultisetIntersectImplementation(第二个,comparer);
}
私有静态IEnumerable MultiSetterSection实现(
这个IEnumerable第一,IEnumerable第二,
IEqualityComparer(比较器)
{
//验证参数。
Assert(第一个!=null);
Assert(第二个!=null);
Assert(比较器!=null);
//拿到第一本字典。
IDictionary counts=first.GroupBy(t=>t,comparer)。
ToDictionary(g=>g.Key,g.LongCount(),comparer);
//扫描
foreach(以秒计)
{
//伯爵。
长计数;
//如果在a中找到该项。
if(计数TryGetValue(t,超出计数))
{
//这是积极的。
断言(计数>0);
//交出物品。
收益率t;
//减少计数。如果
//0,删除。
如果(--count==0)计数,则删除(t);
}
}
}
请注意,这两种方法都是(如果我在这里破坏Big-O表示法,我很抱歉)O(N+m)
其中N
是第一个数组中的项数,M
是第二个数组中的项数。您只需扫描每个列表一次,并且假设获取哈希代码并查找哈希代码是一个O(1)
(常数)操作。这并不比原来的解决方案好:仍然是O(N^2)教我在睡觉前发布…在更深入地研究Contains方法时,它确实执行列表的内部迭代。在这种情况下,Dictionary对象可能是最好的
// The items that match.
IList<int> matched = new List<int>();
// Scan
foreach (int b in ListB)
{
// The count.
int count;
// If the item is found in a.
if (countsOfA.TryGetValue(b, out count))
{
// This is positive.
Debug.Assert(count > 0);
// Add the item to the list.
matched.Add(b);
// Decrement the count. If
// 0, remove.
if (--count == 0) countsOfA.Remove(b);
}
}
public static IEnumerable<T> MultisetIntersect(this IEnumerable<T> first,
IEnumerable<T> second)
{
// Call the overload with the default comparer.
return first.MultisetIntersect(second, EqualityComparer<T>.Default);
}
public static IEnumerable<T> MultisetIntersect(this IEnumerable<T> first,
IEnumerable<T> second, IEqualityComparer<T> comparer)
{
// Validate parameters. Do this separately so check
// is performed immediately, and not when execution
// takes place.
if (first == null) throw new ArgumentNullException("first");
if (second == null) throw new ArgumentNullException("second");
if (comparer == null) throw new ArgumentNullException("comparer");
// Defer execution on the internal
// instance.
return first.MultisetIntersectImplementation(second, comparer);
}
private static IEnumerable<T> MultisetIntersectImplementation(
this IEnumerable<T> first, IEnumerable<T> second,
IEqualityComparer<T> comparer)
{
// Validate parameters.
Debug.Assert(first != null);
Debug.Assert(second != null);
Debug.Assert(comparer != null);
// Get the dictionary of the first.
IDictionary<T, long> counts = first.GroupBy(t => t, comparer).
ToDictionary(g => g.Key, g.LongCount(), comparer);
// Scan
foreach (T t in second)
{
// The count.
long count;
// If the item is found in a.
if (counts.TryGetValue(t, out count))
{
// This is positive.
Debug.Assert(count > 0);
// Yield the item.
yield return t;
// Decrement the count. If
// 0, remove.
if (--count == 0) counts.Remove(t);
}
}
}