Design a leaderboard with the following functions:
addScore(playerId, score)
: Record the player's score to the leaderboard. If the player already exists on the leaderboard, add the new score to the player's existing score.top(K)
: Return the score sum of the top K
players.reset(playerId)
: Reset the player's score to 0
. If the player does not exist, do nothing.Implement the Leaderboard
class:
Leaderboard()
Initializes the object of this class.void addScore(int playerId, int score)
Adds score
to the given playerId
.int top(int K)
Returns the score sum of the top K
players.void reset(int playerId)
Resets the score of the player with the given playerId
to 0
.
Example 1:
Input
["Leaderboard", "addScore", "addScore", "addScore", "addScore", "addScore", "top", "reset", "reset", "addScore", "top"]
[[], [1, 73], [2, 56], [3, 39], [4, 51], [5, 4], [1], [1], [2], [2, 30], [2]]
Output
[null, null, null, null, null, null, 73, null, null, null, 83]
Explanation
Leaderboard leaderboard = new Leaderboard();
leaderboard.addScore(1, 73); // leaderboard = [[1,73]];
leaderboard.addScore(2, 56); // leaderboard = [[1,73],[2,56]];
leaderboard.addScore(3, 39); // leaderboard = [[1,73],[2,56],[3,39]];
leaderboard.addScore(4, 51); // leaderboard = [[1,73],[2,56],[3,39],[4,51]];
leaderboard.addScore(5, 4); // leaderboard = [[1,73],[2,56],[3,39],[4,51],[5,4]];
leaderboard.top(1); // returns 73; // leaderboard = [[1,73],[2,56],[3,39],[4,51],[5,4]];
leaderboard.reset(1); // leaderboard = [[1,0],[2,56],[3,39],[4,51],[5,4]];
leaderboard.reset(2); // leaderboard = [[1,0],[2,0],[3,39],[4,51],[5,4]];
leaderboard.addScore(2, 30); // leaderboard = [[1,0],[2,30],[3,39],[4,51],[5,4]];
leaderboard.top(2); // returns 90; // leaderboard = [[1,0],[2,30],[3,39],[4,51],[5,4]];
Constraints:
1 <= playerId, K <= 104
It is guaranteed that K is less than or equal to the current number of players.
1 <= score <= 100
1000
calls will be made to each function.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
For the Leaderboard problem, the brute force approach is all about trying every possible combination of updates and scores. We will keep track of everything, no matter how inefficient. This guarantees that we will find the correct result, even if it takes a long time.
Here's how the algorithm would work step-by-step:
class Leaderboard:
def __init__(self):
self.score_changes = {}
def addScore(self, playerId: int, score: int) -> None:
if playerId not in self.score_changes:
self.score_changes[playerId] = []
self.score_changes[playerId].append(score)
def top(self, k: int) -> int:
player_scores = {}
for playerId, score_list in self.score_changes.items():
player_scores[playerId] = sum(score_list)
# Sort players based on their total scores.
sorted_players = sorted(player_scores.items(), key=lambda item: item[1], reverse=True)
top_scores_sum = 0
for i in range(min(k, len(sorted_players))):
top_scores_sum += sorted_players[i][1]
return top_scores_sum
def reset(self, playerId: int) -> None:
# Clearing score changes to reset score to 0
self.score_changes[playerId] = []
# Your Leaderboard object will be instantiated and called as such:
# leaderboard = Leaderboard()
# leaderboard.addScore(playerId, score)
# leaderboard.top(K)
# leaderboard.reset(playerId)
The goal is to create a system that keeps track of player scores and quickly provides rankings. We'll use a clever way to store scores so we can easily find the top players without checking every score each time.
Here's how the algorithm would work step-by-step:
class Leaderboard:
def __init__(self):
self.player_scores = {}
self.sorted_scores = []
def addScore(self, player_id, score):
if player_id in self.player_scores:
self.player_scores[player_id] += score
total_score = self.player_scores[player_id]
self._update_score(player_id, total_score)
else:
self.player_scores[player_id] = score
self._insert_score(player_id, score)
def _insert_score(self, player_id, score):
# Maintain sorted order
index = 0
while index < len(self.sorted_scores) and score <= self.sorted_scores[index][1]:
index += 1
self.sorted_scores.insert(index, (player_id, score))
def _update_score(self, player_id, new_score):
# Remove the old score and insert the updated score.
for i, (player, score) in enumerate(self.sorted_scores):
if player == player_id:
del self.sorted_scores[i]
break
self._insert_score(player_id, new_score)
def top(self, k):
# Sum the top k scores.
total = 0
for i in range(k):
total += self.sorted_scores[i][1]
return total
def reset(self, player_id):
# Reset a players score and remove from score list.
if player_id in self.player_scores:
del self.player_scores[player_id]
for i, (player, score) in enumerate(self.sorted_scores):
if player == player_id:
del self.sorted_scores[i]
break
Case | How to Handle |
---|---|
playerId is null or undefined | Throw an IllegalArgumentException, or return an error code indicating an invalid playerId. |
score is negative | Throw an IllegalArgumentException as scores are defined as non-negative integers. |
k is zero | Return 0 as the sum of the top 0 scores is zero. |
k is larger than the number of players in the leaderboard | Return the sum of all scores in the leaderboard, as that is the sum of the top k if k is larger than the leaderboard size. |
Adding the same playerId multiple times. | The score should be overwritten with the new score; handle accordingly. |
Integer overflow when summing top k scores. | Use a larger data type (e.g., long) to store the sum to prevent potential overflow issues. |
Very large number of players and frequent updates. | Optimize the data structure for efficient add, update, and top-k retrieval operations, such as using a priority queue or balanced tree. |
playerId is an empty string | Treat it as an invalid input like null playerId and return an error or throw an exception. |