С [1 119] LINQ, это тривиально, как можно звонить Intersect
дополнительный метод на Enumerable
класс , чтобы дать Вам пересечение набора двух массивов:
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 }
Затем Вы оказываетесь перед необходимостью поддерживать количество объектов сами и урожая/декремента, поскольку Вы сканируете два списка.
Первый, Вы хотели бы собраться Dictionary<TKey, int>
со списками отдельных объектов:
var countsOfA = ListA.GroupBy(i => i).ToDictionary(g => g.Key, g => g.Count());
Оттуда, можно просканировать ListB
и место, что в списке при случайной встрече с объектом в [1 114]:
// 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);
}
}
}
Примечание, которое оба из этих подходов (и я приношу извинения, если я забиваю Нотацию "большого О" здесь) O(N + M)
, где N
количество объектов в первом массиве, и M
, является количеством объектов во втором массиве. Необходимо просканировать каждый список только однажды, и предполагается, что получение хэш-кодов и выполнение поисков на хэш-кодах O(1)
(постоянная) операция.
Загрузите всю Листу в экземпляр HashSet и затем протестируйте foreach объект в ListB против HastSet: я вполне уверен, что это было бы O (N).
//untested code ahead
HashSet<int> hashSet = new HashSet<int>(ListA);
foreach (int i in ListB)
{
if (hashSet.Contains(i))
return true;
}
Вот то же самое в одной строке:
return new HashSet<int>(ListA).Overlaps(ListB);
HashSet не существует в.NET 3.5, таким образом, в.NET 2.0 можно использовать Dictionary<int,object>
(вместо того, чтобы использовать HashSet<int>
) и всегда хранить пустой указатель как объект/значение в Словаре, так как Вы только интересуетесь ключами.
Вместо того, чтобы выполнить итерации через каждый список, смотрите на эти Список. Содержит метод:
foreach (int a in ListA)
{
if (ListB.Contains(a))
return true;
}
Как насчет того, чтобы использовать метод BinarySearch вместо того, чтобы выполнить итерации по всем элементам во внутреннем цикле?
Chris дает O (N) решение путем хеширования. Теперь, в зависимости от постоянного множителя (из-за хеширования), это могло бы быть достойно рассмотрения O (N журнал (N)) решение путем сортировки. Существует несколько различных вариантов, которые можно рассмотреть в зависимости от варианта использования.
Вид ListB (O (N журнал (N)), и использование ищущий алгоритм для парсинга каждого элемента в Листе (который является снова O (N) * O (журнал (N))).
Вид и Листа и ListB (O (N журнал (N)), и использование O (N) алгоритм для сравнения этих списков для дубликатов.
, Если оба списка будут используемыми несколько раз, второй метод предпочтен.