有没有办法找到比这更有效/更快的最小值索引?
int minimumValueIndex = List.IndexOf(List.Min());
有没有办法找到比这更有效/更快的最小值索引?
int minimumValueIndex = List.IndexOf(List.Min());
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;
}
以我自己的经验,诸如 Array.Max() 和 Array.Min() 之类的 LINQ 聚合方法通常比手动 for 循环慢。因此,您可以考虑这样的替代方法:
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 在您的环境中测试这两种方法的速度。
@cdhowie 发布的答案存在问题,因为它假设 anIList<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 ;
}
您也可以使用库存的 LinqAggregate()
重载,尽管它并不比蛮力方法更清洁或更简单(恕我直言,效率可能也较低):
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 ;
目前 .NET 支持允许简单解决方案的值元组:
var index = List.Select((item, index) => (item, index)).Max().index;
如果可能,在将值放入列表时跟踪最小值/索引,因此您无需遍历完整列表。然后,如果将新值添加到列表中,请根据您保存的最小值检查它们,并根据需要更改新的最小值。
当然,这可能不适合您的情况。
我稍微改进了@cdhowie 的答案以使其更强大。如果有多个最小元素,则此方法将返回第一个。
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;
}
最小计算:在集合中查找Min
值不能比 O(n) 更快,因此它可能没有比这更好的方法,只是代码风格不同。
查找步骤:根据您的问题,您可能会使用一些特殊的数据结构(例如二叉树、堆树等),这样您可以更快地找到索引。
使用诸如最小堆树之类的东西,您可以通过 O(1) 获得最小值,而无需使用一些特殊的添加、删除功能。
当然。只需编写自己的Min
函数,而不是使用 LINQ,来跟踪当前最小值的索引。通过这样做,您可以避免需要根据它的最小值查找项目的索引。
我很懒所以我会使用:
List.Sort();/* sorts the values least to greatest */
List[0];/* first item has the least value */