快速/有效地获取List <t>中的最小值索引?</t>

时间:2013-05-01 17:41:02

标签: c# list optimization .net-3.5

有没有办法找到比这更有效/更快的最小价值指数?

int minimumValueIndex = List.IndexOf(List.Min());

9 个答案:

答案 0 :(得分:8)

是的,您可以通过构建自定义List.IndexOf()扩展程序来消除Min()的开销。 (实际上,Enumerable.Min()应该有一个扩展,按键选择原始元素,而不是选择转换。在这种情况下,这种疏忽特别痛苦。)

public static int IndexOfMin(this IList<int> self)
{
    if (self == null) {
        throw new ArgumentNullException("self");
    }

    if (self.Count == 0) {
        throw new ArgumentException("List is empty.", "self");
    }

    int min = self[0];
    int minIndex = 0;

    for (int i = 1; i < self.Count; ++i) {
        if (self[i] < min) {
            min = self[i];
            minIndex = i;
        }
    }

    return minIndex;
}

答案 1 :(得分:4)

根据我自己的经验,LINQ聚合方法(如Array.Max()和Array.Min())通常比手动循环慢。所以,你可以考虑这样的事情作为另一种方法:

int minima=0;
int mindex=0;

for(int i=0;i<List.Count;i++)
{
    if (List[i]<minima)
        {minima=List[i]; mindex=i;}
}

您始终可以使用System.Diagnostics.StopWatch测试环境中两种方法的速度。

答案 2 :(得分:2)

@cdhowie发布的答案存在问题,因为它假设IList<T>可以通过其索引器有效地访问特定项目。虽然这对于数组和List[T]都是正确的,但它是以无法保证的(例如,一个实现Ilist<T>的单链表。)

如果我要以通用的Linqy方式执行此操作,我会执行以下操作:

public static IndexOfMinValue<T>( this IList<T> list ) where T:IComparable
{
  if ( list == null ) throw new ArgumentNullException("list") ;
  int? offset = null ;
  T    min    = default(T) ;

  int i = 0 ;
  foreach ( T item in list )
  {
    if ( !offset.HasValue || item.CompareTo(min) < 0 )
    {
       offset = i ;
       min    = item ;
    }
    ++i ;
  }

  if ( !offset.HasValue ) throw new ArgumentOutOfRangeException("list","list is empty") ;
  return offset.Value ;
}

或者,可以说更清晰,因为我们摆脱了无关的初始化和循环体内的无关比较:

public static int IndexOfMin<T>( this IList<T> list ) where T:IComparable
{
  if ( list == null ) throw new ArgumentNullException("list") ;

  IEnumerator<T> enumerator  = list.GetEnumerator() ;
  bool           isEmptyList = ! enumerator.MoveNext() ;

  if ( isEmptyList ) throw new ArgumentOutOfRangeException("list","list is empty") ;

  int minOffset = 0 ;
  T   minValue  = enumerator.Current ;
  for ( int i = 1 ; enumerator.MoveNext() ; ++i )
  {
    if ( enumerator.Current.CompareTo(minValue) >= 0 ) continue ;
    minValue  = enumerator.Current ;
    minOffset = i ;
  }

  return minOffset ;
}

你也可以使用股票Linq Aggregate()重载,虽然它不比蛮力方法更清晰或更简单(可能效率也不高,恕我直言):

IList<int> = GetSomeIntegers() ;

int minIndex = list.Aggregate( (Tuple<int,int,int>)null,
  ( acc , item ) => {
    int offset     = 0    ;
    int minValue   = item ;
    int minOffset  = 0    ;
    if ( acc != null )
    {
      offset    = acc.Item3 + 1 ;
      minValue  = item < acc.Item1 ? item   : acc.Item1 ;
      minOffset = item < acc.Item1 ? offset : acc.Item2 ;
    }
    return new Tuple<int, int, int>( minValue , minOffset , offset ) ;
  }).Item2 ;

答案 3 :(得分:1)

如果可能,跟踪最小值/索引,因为值已放入列表中,因此您无需遍历完整列表。然后,如果将新值添加到列表中,请根据您保存的最小值检查它们,并根据需要更改新的最小值。

当然,这可能不适合您的情况。

答案 4 :(得分:1)

我稍微改进了@ cdhowie的answer以使其更强大。如果有多个最小元素,则此方法将返回第一个元素。

public static T GetMin<T, TOrder>(this IEnumerable<T> self, Func<T, TOrder> orderFunc, 
                                  out int minIndex, IComparer<TOrder> cmp = null)
{
    if (self == null) throw new ArgumentNullException("self");            

    IEnumerator<T> selfEnumerator = self.GetEnumerator();
    if (!selfEnumerator.MoveNext()) throw new ArgumentException("List is empty.", "self");

    if (cmp == null) cmp = Comparer<TOrder>.Default;

    T min = selfEnumerator.Current;
    minIndex = 0;
    int intCount = 1;
    while (selfEnumerator.MoveNext ())
    {
        if (cmp.Compare(orderFunc(selfEnumerator.Current), orderFunc(min)) < 0)
        {
            min = selfEnumerator.Current;
            minIndex = intCount;
        }
        intCount++;
    }

    return min;
}

答案 5 :(得分:1)

当前.NET支持值元组,该值元组允许简单的解决方案:

var index = List.Select((item, index) => (item, index)).Max().index;

答案 6 :(得分:0)

最小计算:查找集合中的Min值不能比O(n)更快完成,所以它可能没有比这更好的方法,但只是代码风格的不同之处

查找步骤:根据您的问题,您可以使用一些特殊的数据结构(例如二叉树,堆树等),这样您就可以更快地找到索引。

  

使用像min-heap tree这样的东西你可以用O(1)获得最小值,而不需要使用一些特殊的Add,Remove函数。

答案 7 :(得分:0)

不确定。只需编写自己的Min函数,而不是使用LINQ,它跟踪当前最小值的索引。通过这样做,您可以避免需要根据项目的最小值找到项目的索引。

答案 8 :(得分:-1)

我很懒,所以我会用:

List.Sort();/* sorts the values least to greatest */
List[0];/* first item has the least value */