Problem overview
The function topScorer gets scores, an unordered_map<string, int> from player names to scores, and returns the name of the player with the highest score. If several players share the highest score, it returns the name that comes first alphabetically. The map has at least one entry and every score is between 0 and 100.
For {"Ana": 90, "Ben": 75, "Cara": 88} the answer is Ana. For {"Zoe": 82, "Ana": 82} both players tie, and Ana comes before Zoe. For {"Max": 0} the only player wins with a score of 0.
Approach
Keep the best player seen so far and compare every entry with it. This is the same "best so far" pattern used to find the largest number in a vector, with two differences. The value to compare is a map value, and a tie is settled by the key.
A map has no fixed order, so the code can't say "the first player wins a tie". It has to compare the names explicitly, and then the result is the same whatever order the loop visits the entries in.
Here is how the comparison works for two players with the same score, Zoe and Ana, both at 82. Whichever one the loop reaches first becomes the best, because 82 beats -1. When the other one arrives, the score is equal, so the name decides: if Ana is already the best, "Zoe" < "Ana" is false and nothing changes. If Zoe is the best, "Ana" < "Zoe" is true, so Ana takes over. Either way the function ends with Ana.
Step by step
- Set up the best so far.
bestNamestarts as an empty string andbestScorestarts at -1. Every real score is at least 0, so the first player always beats -1. - Visit every entry with
for (auto& [name, score] : scores). - Check whether this player is better: a higher score, or an equal score with a name that sorts earlier. On two
stringvalues,<is true when the left one comes first in alphabetical order. - Take over the title. Store the name and the score in
bestNameandbestScore. - Return
bestNameafter the loop.
The solution in C++
class Solution {
public:
string topScorer(unordered_map<string, int>& scores) {
string bestName = "";
int bestScore = -1;
for (auto& [name, score] : scores) {
if (score > bestScore || (score == bestScore && name < bestName)) {
bestName = name;
bestScore = score;
}
}
return bestName;
}
};
A few details:
- The map is passed by reference, so a big map isn't copied. The function only reads it.
&&binds tighter than||, so the parentheses around the tie check don't change the result. They make it clear that the name only matters when the scores are equal.- Names are compared character by character. All names here are a capital letter followed by lowercase letters, so this is alphabetical order.
Edge cases and common mistakes
- Starting
bestScoreat 0. A score of 0 is allowed. For{"Max": 0}the first player scores 0, which doesn't beat 0, and the tie check"Max" < ""is false, so the function returns an empty string instead ofMax. Starting at -1 avoids it. - Relying on the loop order. An
unordered_mapcan visitZoebeforeAnaor after her. Only the explicit name comparison makes the tie result stable. - Returning the score. The function returns the name, not the number.
- Ties at zero. With
{"Max": 0, "Bo": 0}the function returnsBo.
Testing
{"Ana": 90, "Ben": 75, "Cara": 88}givesAna.{"Zoe": 82, "Ana": 82}givesAna.{"Max": 0}givesMax.{"Max": 0, "Bo": 0}givesBo.
