2009-07-16 21 views
18

Cho một danh sách như vậy:Lấy Index của Maximum tử

 List<int> intList = new List<int>(); 
     intList.Add(5); 
     intList.Add(10); 
     intList.Add(15); 
     intList.Add(46); 

làm thế nào để bạn có được những chỉ số của phần tử tối đa trong danh sách? Trong trường hợp này, nó ở chỉ mục 3.

Chỉnh sửa: Thật xấu hổ khi LINQ tiêu chuẩn không gửi chức năng này.

Trả lời

15

Đây là một * đơn giản và giải pháp ** tương đối hiệu quả:

int indexMax 
    = !intList.Any() ? -1 : 
    intList 
    .Select((value, index) => new { Value = value, Index = index }) 
    .Aggregate((a, b) => (a.Value > b.Value) ? a : b) 
    .Index; 
  1. !intList.Any() ? -1 : sẽ buộc -1 nếu danh sách trống;

  2. Select sẽ chiếu mỗi phần tử int vào một loại ẩn danh với hai thuộc tính: ValueIndex;

  3. Aggregate sẽ lấy phần tử có số cao nhất Value;

  4. Cuối cùng, chúng tôi nhận được Index của phần tử đã chọn.

* Tính đơn giản là tương đối. Mục tiêu ở đây là đạt được sự cân bằng về khả năng đọc và vẫn chỉ quét danh sách một lần.

** Việc phân bổ nhiều đối tượng mới trong Select có thể lãng phí. Như một số người đã thử nghiệm, nó không hoạt động tốt cho các danh sách lớn.

CHỈNH SỬA 1: kiểm tra danh sách trống.

CHỈNH SỬA 2: thêm thông báo trước về hiệu suất.

+2

Không đơn giản, không nhanh. – watbywbarif

+1

Có thể nó không đơn giản như bạn muốn, nhưng nó được trình bày nhanh nhất ở đây. Giải nén nó vào một phương pháp mở rộng và quên nó đi. Tôi rất muốn thấy sự lựa chọn của bạn. Downvoting câu trả lời tốt nhất bởi vì bạn không thích nó chỉ là trẻ con. – jpbochi

+0

Nếu nó nhanh hơn Matt Howells và Jon Skeets tôi sẽ ăn năn bản thân mình và không bao giờ sử dụng lại. – watbywbarif

26

theo cách này:

var maxIndex = foo.IndexOf(foo.Max()); 
+3

Điều này gọn gàng và ngắn gọn, nhưng yêu cầu tối đa hai lần truyền đầy đủ thông qua danh sách để lấy chỉ mục. Nếu bạn cần nó nhanh hơn, bạn nên sử dụng vòng lặp for và theo dõi chỉ mục khi bạn đi. –

+5

Nó vẫn còn O (n), và vượt qua thứ hai có thể không phải là một vượt qua đầy đủ. Tôi sẽ đi với mã này nếu hiệu suất nếu không phải là một vấn đề lớn. –

+1

oneliners không bao giờ mất đi sự quyến rũ của chúng. +1 – heltonbiker

0

Sử dụng một chức năng tùy chỉnh, sử dụng Max() và IndexOf() chi phí nhiều hơn.

1

Dưới đây là phương pháp phi LINQ nếu bạn thích:

private int ReturnMaxIdx(List<int> intList) 
     { 
      int MaxIDX = -1; 
      int Max = -1; 

      for (int i = 0; i < intList.Count; i++) 
      { 
       if (i == 0) 
       { 
        Max = intList[0]; 
        MaxIDX = 0; 
       } 
       else 
       { 
        if (intList[i] > Max) 
        { 
         Max = intList[i]; 
         MaxIDX = i; 
        } 
       } 
      } 

      return MaxIDX; 
     } 

Đây là một pass duy nhất thông qua danh sách ít nhất.

Hope this helps,

Kyle

11

Dưới đây là một phương pháp LINQ tùy chỉnh mà tôi tin rằng những gì bạn muốn. (Trước đây tôi đã khác mà không một công chiếu, nhưng bạn chỉ có thể gọi Chọn để làm điều đó, vì bạn chỉ cần chỉ số.)

public static int MaxIndex<T>(this IEnumerable<T> source) 
{ 
    IComparer<T> comparer = Comparer<T>.Default; 
    using (var iterator = source.GetEnumerator()) 
    { 
     if (!iterator.MoveNext()) 
     { 
      throw new InvalidOperationException("Empty sequence"); 
     } 
     int maxIndex = 0; 
     T maxElement = iterator.Current; 
     int index = 0; 
     while (iterator.MoveNext()) 
     { 
      index++; 
      T element = iterator.Current; 
      if (comparer.Compare(element, maxElement) > 0) 
      { 
       maxElement = element; 
       maxIndex = index; 
      } 
     } 
     return maxIndex; 
    } 
} 
+0

Trông giống như ý tưởng đúng (cái nhìn lướt qua, đến muộn). Tôi muốn sử dụng 'selector' làm tên của tham số thứ hai của bạn để phù hợp với các phương thức Enumerable.Max(). –

+0

@ 280Z28: Tôi nghĩ bạn phải xem xét phiên bản cũ - Tôi đã loại bỏ bit chọn ... –

3

tôi không thể cải thiện về câu trả lời của Jon Skeet cho trường hợp tổng quát, vì vậy Tôi sẽ cho giải thưởng 'hiệu suất cao' trong trường hợp cụ thể của một danh sách các int.

public static class Extensions 
{ 
    public static int IndexOfMaximumElement(this IList<int> list) 
    { 
     int size = list.Count; 

     if (size < 2) 
      return size - 1; 

     int maxValue = list[0]; 
     int maxIndex = 0; 

     for (int i = 1; i < size; ++i) 
     { 
      int thisValue = list[i]; 
      if (thisValue > maxValue) 
      { 
       maxValue = thisValue; 
       maxIndex = i; 
      } 
     } 

     return maxIndex; 
    } 
7

Đây là cách thực hiện trong một (dài) dòng bằng LINQ, chỉ với một lần đi qua bộ sưu tập. Nó sẽ làm việc cho bất kỳ IEnumerable<int>, không chỉ danh sách.

int maxIndex = intList 
    .Select((x, i) => new { Value = x, Index = i }) 
    .Aggregate 
     (
      new { Value = int.MinValue, Index = -1 }, 
      (a, x) => (a.Index < 0) || (x.Value > a.Value) ? x : a, 
      a => a.Index 
     ); 

Dưới đây là tương đương phi LINQ ở trên, sử dụng một vòng lặp foreach. (Một lần nữa, chỉ cần một lần đi qua bộ sưu tập và sẽ hoạt động cho bất kỳ IEnumerable<int> nào.)

int maxIndex = -1, maxValue = int.MinValue, i = 0; 
foreach (int v in intList) 
{ 
    if ((maxIndex < 0) || (v > maxValue)) 
    { 
     maxValue = v; 
     maxIndex = i; 
    } 
    i++; 
} 

Nếu bạn biết rằng bộ sưu tập là một IList<int> sau đó một đồng bằng for vòng lặp có lẽ là giải pháp đơn giản nhất:

int maxIndex = -1, maxValue = int.MinValue; 
for (int i = 0; i < intList.Count; i++) 
{ 
    if ((maxIndex < 0) || (intList[i] > maxValue)) 
    { 
     maxValue = intList[i]; 
     maxIndex = i; 
    } 
} 
0

Đây là giải pháp của tôi:

public static int IndexOfMax(this IList<int> source) 
{ 
    if (source == null) 
     throw new ArgumentNullException("source"); 
    if (source.Count == 0) 
     throw new InvalidOperationException("List contains no elements"); 

    int maxValue = source[0]; 
    int maxIndex = 0; 
    for (int i = 1; i < source.Count; i++) 
    { 
     int value = source[i]; 
     if (value > maxValue) 
     { 
      maxValue = value; 
      maxIndex = i; 
     } 
    } 
    return maxIndex; 
} 
0
public static class Extensions 
{ 
    public static int MaxIndex<T>(this IEnumerable<T> TSource) 
    { 
     int i = -1; 
     using (var iterator = TSource.GetEnumerator()) 
      while (iterator.MoveNext()) 
       i++; 
     return i; 
    } 
} 

Dưới đây là crack của tôi ở vấn đề này. Tôi trả về -1 thay vì ném một ngoại lệ vì đây là những gì hàm FindIndex làm và tôi thấy nó rất tiện lợi.

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