2010-11-17 31 views
15

Xét rằng đây là một nhiệm vụ rất cơ bản, tôi không thể nghĩ ra một cách dễ dàng để làm điều đó. Làm thế nào bạn sẽ nhận được chỉ số của giá trị thấp nhất trong một mảng int? Sử dụng LINQ/MoreLinq là có thể. Tôi không thể tìm thấy một lớp lót hợp lý cho đến nay.Bạn sẽ nhận được chỉ mục giá trị thấp nhất trong một mảng int như thế nào?

+2

Có thể có số trùng lặp trong mảng và trong trường hợp nào, bạn muốn hiển thị chỉ số nào nếu có hai số thấp nhất ? – Paddy

+0

@Paddy Có bản sao có thể. Bất kỳ điều nào trong số này đều tốt để được trả lại, mặc dù hành vi nhất quán (bất kỳ) nào sẽ được đánh giá cao (ví dụ: hành vi cuối cùng). – mafu

Trả lời

17

Kể từ khi bạn đề cập đến MoreLinq, làm thế nào về:

int[] array = .. 

// Will throw if the array is empty. 
// If there are duplicate minimum values, the one with the smaller 
// index will be chosen. 
int minIndex = array.AsSmartEnumerable() 
        .MinBy(entry => entry.Value) 
        .Index; 

Một thay thế:

// Will throw if the array is empty. 
// Requires two passes over the array. 
int minIndex = Array.IndexOf(array, array.Min()); 

Bạn có thể tất nhiên viết riêng mở rộng-phương pháp của bạn:

// Returns last index of the value that is the minimum. 
public static int IndexOfMin(this IEnumerable<int> source) 
{ 
    if(source == null) 
    throw new ArgumentNullException("source"); 

    int minValue = int.MaxValue; 
    int minIndex = -1; 
    int index = -1; 

    foreach(int num in source) 
    { 
     index++; 

     if(num <= minValue) 
     { 
     minValue = num; 
     minIndex = index; 
     } 
    } 

    if(index == -1) 
    throw new InvalidOperationException("Sequence was empty"); 

    return minIndex; 
} 

Với một số nỗ lực , bạn có thể khái quát hóa điều này thành bất kỳ loại nào bằng cách chấp nhận một IComparer<T>, mặc định là Comparer<T>.Default.

+0

Tại sao bạn đã chọn foreach hơn trong phương pháp mở rộng? – mafu

+0

Nói chung, các chuỗi không thể được truy cập bằng chỉ mục. Sử dụng 'foreach' là cách bình thường để lặp lại một chuỗi tùy ý. Bạn * có thể * nhận được điều tra và sau đó sử dụng một vòng lặp 'for', nhưng điều đó sẽ trông khá lộn xộn. Nếu bạn thực sự cần điều tra viên, một vòng lặp 'while' phổ biến hơn nhiều. Trong trường hợp này, không cần thiết. – Ani

+0

Rất tiếc, có, tôi đã vô tình nghĩ đến 'nguồn' làm mảng. – mafu

4

Không rất nhớ thân thiện, nhưng ...

array.Select((n, i) => new { index = i, value = n }) 
    .OrderBy(item => item.value) 
    .First().index 
+0

Bạn cũng có thể thay thế OrderBy/First bằng MinBy. – mafu

+1

@mafutrct: yep nếu bạn có MoreLinq, mà bạn làm nhưng tôi không :) –

1

Nó xấu xí nhưng nó chỉ cần một đường chuyền đơn thông qua trình tự và chỉ sử dụng phương pháp khung built-in:

int index = yourArray.Select((x, i) => new { Val = x, Idx = i }) 
        .Aggregate(new { Val = -1, Idx = -1 }, 
           (a, x) => (x.Idx == 0 || x.Val < a.Val) ? x : a, 
           x => x.Idx); 

Và, tất nhiên, bạn có thể viết một phương pháp mở rộng đa năng:

int index = yourArray.MinIndex(); 

// ... 

public static class EnumerableExtensions 
{ 
    public static int MinIndex<T>(
     this IEnumerable<T> source, IComparer<T> comparer = null) 
    { 
     if (source == null) 
      throw new ArgumentNullException("source"); 

     if (comparer == null) 
      comparer = Comparer<T>.Default; 

     using (var enumerator = source.GetEnumerator()) 
     { 
      if (!enumerator.MoveNext()) 
       return -1; // or maybe throw InvalidOperationException 

      int minIndex = 0; 
      T minValue = enumerator.Current; 

      int index = 0; 
      while (enumerator.MoveNext()) 
      { 
       index++; 
       if (comparer.Compare(enumerator.Current, minValue) < 0) 
       { 
        minIndex = index; 
        minValue = enumerator.Current; 
       } 
      } 
      return minIndex; 
     } 
    } 
} 
8

LINQ có lẽ không phải là giải pháp tốt nhất hoặc vấn đề này, nhưng đây là một biến thể khác là O (n). Nó không sắp xếp và chỉ duyệt qua mảng một lần.

var arr = new int[] { 3, 1, 0, 5 }; 
int pos = Enumerable.Range(0, arr.Length) 
    .Aggregate((a, b) => (arr[a] < arr[b]) ? a : b); // returns 2 

Cập nhật: Trả lời câu hỏi ban đầu trực tiếp, đây là cách tôi sẽ làm điều đó:

var arr = new int[] { 3, 1, 0, 5 }; 
int pos = 0; 
for (int i = 0; i < arr.Length; i++) 
{ 
    if (arr[i] < arr[pos]) { pos = i; } 
} 
// pos == 2 

Không, nó không sử dụng LINQ. Có, đó là nhiều hơn một dòng. Nhưng nó thực sự đơn giản và rất nhanh. Làm cho nó thành một phương pháp nhỏ bé và gọi nó từ bất cứ đâu trên một dòng: pos = FindMinIndex(arr);

+0

Đây phải là câu trả lời. Nhanh hơn và đơn giản hơn nhiều! – RMalke

+0

@RMalke Nhanh hơn bao nhiêu? – shoelzer

+0

Tôi đã không đo lường, nhưng tôi đã chọn ((x, i) => ...) OrderBy (...). Đầu tiên (...) và gọi nó chính xác lần 15257190400. Tôi đã nhanh hơn đáng kể – RMalke

Các vấn đề liên quan