在C#中跨多个列表查找通用项目的最快方法

时间:2020-03-05 18:46:52  来源:igfitidea点击:

给定以下内容:

List<List<Option>> optionLists;

确定所有N个列表中出现的Option对象子集的快速方法是什么?相等性是通过某些字符串属性(例如option1.Value == option2.Value)确定的。

因此,我们应该以List &lt;Option>结尾,其中每个项目仅出现一次。

解决方案

回答

好的,这将找到在每个列表中都有一个值的Option对象的列表。

var x = from list in optionLists
        from option in list
        where optionLists.All(l => l.Any(o => o.Value == option.Value))
        orderby option.Value
        select option;

它不会进行"不同"选择,因此它将返回多个Option对象,其中一些具有相同的Value。

回答

排序,然后执行类似于合并排序的操作。

基本上,我们可以这样做:

  • 从每个列表中检索第一项
  • 比较项目,如果相等,则输出
  • 如果任何一项都在其他项之前,请从相应的列表中按顺序检索新项以将其替换;否则,从所有列表中检索新项以将其全部替换
  • 只要仍然有商品,请返回2.

回答

使用hashSet怎么办?这样,我们就可以在O(n)中执行所需的操作,其中n是所有列表中所有项的总和,我认为这是最快的方法。

我们只需要遍历每个列表并将找到的值插入哈希集
当我们插入一个已经存在的键时,我们将收到false作为.add方法的返回值,否则返回true

回答

这是一个更有效的实现:

static SortedDictionary<T,bool>.KeyCollection FindCommon<T> (List<List<T>> items)
{
  SortedDictionary<T, bool>
    current_common = new SortedDictionary<T, bool> (),
    common = new SortedDictionary<T, bool> ();

  foreach (List<T> list in items)
  {
    if (current_common.Count == 0)
    {
      foreach (T item in list)
      {
        common [item] = true;
      }
    }
    else
    {
      foreach (T item in list)
      {
        if (current_common.ContainsKey(item))
          common[item] = true;
        else
          common[item] = false;
      }
    }

    if (common.Count == 0)
    {
      current_common.Clear ();
      break;
    }

    SortedDictionary<T, bool>
      swap = current_common;

    current_common = common;
    common = swap;
    common.Clear ();
  }

  return current_common.Keys;
}

它的工作方式是创建一组到目前为止已处理的所有列表共有的所有项目,并将每个列表与此集合进行比较,创建当前列表共有的临时项目和到目前为止的常见项目的列表的临时集合。实际上是O(n.m),其中n是列表数,m是列表中的项数。

使用它的一个例子:

static void Main (string [] args)
{
  Random
    random = new Random();

  List<List<int>>
    items = new List<List<int>>();

  for (int i = 0 ; i < 10 ; ++i)
  {
    List<int>
      list = new List<int> ();

    items.Add (list);

    for (int j = 0 ; j < 100 ; ++j)
    {
      list.Add (random.Next (70));
    }
  }

  SortedDictionary<int, bool>.KeyCollection
    common = FindCommon (items);

  foreach (List<int> list in items)
  {
    list.Sort ();
  }

  for (int i = 0 ; i < 100 ; ++i)
  {
    for (int j = 0 ; j < 10 ; ++j)
    {
      System.Diagnostics.Trace.Write (String.Format ("{0,-4:D} ", items [j] [i]));
    }

    System.Diagnostics.Trace.WriteLine ("");
  }

  foreach (int item in common)
  {
    System.Diagnostics.Trace.WriteLine (String.Format ("{0,-4:D} ", item));
  }
}

回答

我没有性能统计信息,但是如果我们不想使用自己的方法,则各种集合库都有一个" Set"或者" Set(T)"对象,它们提供通常的设置过程。 (按我将其使用的顺序列出)。

  • IESI集合(实际上只是Set类)
  • PowerCollections(暂时不会更新)
  • C5(从不亲自使用)

回答

基于Matt的答案,由于我们只对所有列表共有的选项感兴趣,因此我们可以简单地检查第一个列表中其他选项共享的任何选项:

var sharedOptions =
    from option in optionLists.First( ).Distinct( )
    where optionLists.Skip( 1 ).All( l => l.Contains( option ) )
    select option;

如果选项列表不能包含重复的整体,则不需要"不同"调用。如果列表的大小相差很大,则最好遍历最短列表中的选项,而不要碰巧碰巧是"第一个"列表。排序或者散列的集合可以用来改善"包含"调用的查找时间,尽管对于中等数量的项目而言,这应该不会有太大的区别。