соответствие объектам из двух списков (или массивы)

19
задан casperOne 27 November 2012 в 17:29
поделиться

5 ответов

С [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) (постоянная) операция.

39
ответ дан 30 November 2019 в 02:36
поделиться

Загрузите всю Листу в экземпляр 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>) и всегда хранить пустой указатель как объект/значение в Словаре, так как Вы только интересуетесь ключами.

7
ответ дан 30 November 2019 в 02:36
поделиться

Вместо того, чтобы выполнить итерации через каждый список, смотрите на эти Список. Содержит метод:

foreach (int a in ListA)
{
  if (ListB.Contains(a))
    return true;
}
3
ответ дан 30 November 2019 в 02:36
поделиться

Как насчет того, чтобы использовать метод BinarySearch вместо того, чтобы выполнить итерации по всем элементам во внутреннем цикле?

0
ответ дан 30 November 2019 в 02:36
поделиться

Chris дает O (N) решение путем хеширования. Теперь, в зависимости от постоянного множителя (из-за хеширования), это могло бы быть достойно рассмотрения O (N журнал (N)) решение путем сортировки. Существует несколько различных вариантов, которые можно рассмотреть в зависимости от варианта использования.

  1. Вид ListB (O (N журнал (N)), и использование ищущий алгоритм для парсинга каждого элемента в Листе (который является снова O (N) * O (журнал (N))).

  2. Вид и Листа и ListB (O (N журнал (N)), и использование O (N) алгоритм для сравнения этих списков для дубликатов.

, Если оба списка будут используемыми несколько раз, второй метод предпочтен.

2
ответ дан 30 November 2019 в 02:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: