C#中Array.IndexOf无匹配取最近项及排行榜排名计算优化问题
Hey there! Let's tackle your two C# questions one by one—both are about making array operations more efficient, so let's dive in.
First off, Array.IndexOf only looks for exact matches, so it won't help you find the closest element out of the box. The approach you take depends on whether your array is sorted or not:
如果数组是有序的(推荐,效率更高)
Use binary search to find the insertion point for your target item—this gives you a starting point to compare neighboring elements. C#'s Array.BinarySearch returns the index of an exact match if found; if not, it returns -(insertionPoint) - 1, where insertionPoint is the position where the item would be inserted to keep the array sorted.
Here's a reusable method to find the closest element's index:
public static int FindClosestIndex(int[] sortedArray, int target) { if (sortedArray == null || sortedArray.Length == 0) throw new ArgumentException("Array can't be null or empty!"); int searchResult = Array.BinarySearch(sortedArray, target); if (searchResult >= 0) return searchResult; // Exact match found int insertionPoint = ~searchResult; // Convert negative result to insertion position // Handle edge cases: target is smaller than all elements or larger than all if (insertionPoint == 0) return 0; if (insertionPoint == sortedArray.Length) return sortedArray.Length - 1; // Compare which neighboring element is closer int diffBefore = target - sortedArray[insertionPoint - 1]; int diffAfter = sortedArray[insertionPoint] - target; return diffBefore <= diffAfter ? insertionPoint - 1 : insertionPoint; }
如果数组是无序的
You'll have to iterate through the entire array to track the element with the smallest absolute difference from your target. This is O(n) time, which isn't great for large arrays—if possible, sort the array first (O(n log n)) then use the binary search method above.
Your original for-loop approach is slow because it's O(n*m) time (n = number of scores, m = number of player scores). The fix is to use binary search on the sorted, deduplicated scores array, which drops each player's score lookup to O(log n) time.
Since your scores are already in descending order (and you've deduplicated them), we can implement a custom binary search tailored to descending sorted arrays to calculate ranks directly:
Step 1: Deduplicate the original scores (if you haven't already)
Since the input is already sorted descending, we can just iterate once to remove duplicates:
List<int> uniqueScores = new List<int>(); foreach (int score in scores) { if (uniqueScores.Count == 0 || uniqueScores.Last() != score) { uniqueScores.Add(score); } } int[] sortedDescUnique = uniqueScores.ToArray();
Step 2: Binary search for each player's rank
This method finds the rank of a player's score in the descending, deduplicated array:
private static int GetPlayerRank(int[] sortedDescScores, int playerScore) { int left = 0; int right = sortedDescScores.Length - 1; while (left <= right) { int mid = left + (right - left) / 2; // Avoid overflow if (sortedDescScores[mid] == playerScore) { return mid + 1; // Rank is 1-based index } else if (sortedDescScores[mid] > playerScore) { // Target is smaller than mid, look right left = mid + 1; } else { // Target is larger than mid, look left right = mid - 1; } } // If no match, left is the insertion position—rank is left + 1 (1-based) return left + 1; }
Step 3: Put it all together in the climbingLeaderboard method
static int[] climbingLeaderboard(int[] scores, int[] playerScores) { // Deduplicate scores first List<int> uniqueScores = new List<int>(); foreach (int score in scores) { if (uniqueScores.Count == 0 || uniqueScores.Last() != score) { uniqueScores.Add(score); } } int[] sortedDescUnique = uniqueScores.ToArray(); int[] ranks = new int[playerScores.Length]; for (int i = 0; i < playerScores.Length; i++) { ranks[i] = GetPlayerRank(sortedDescUnique, playerScores[i]); } return ranks; }
Why this works better
- Total time complexity is O(n + m log n) (n for deduplication, m log n for all player lookups) vs. O(n*m) for the original for-loop. This is a massive improvement when dealing with large datasets.
Array.IndexOfisn't useful here because it's a linear search (slow for big arrays) and only returns -1 when no match is found—no built-in way to get rank from that.
内容的提问来源于stack exchange,提问作者Mohamed

