Hello, reader ๐๐ฝ ! Welcome to day 58 of the series on Problem Solving. Through this series, I aim to pick up at least one question everyday and share my approach for solving it.
Today, I will be picking up LeetCode's daily challenge problem: 2225. Find Players With Zero or One Losses.
๐ค Problem Statement
- Given an integer array matches where matches[i] =
[winner, loser]
indicates that the playerwinner
defeated playerloser
in a match, return a list answer of size 2 where:answer[0]
is a list of all players that have not lost any matches.answer[1]
is a list of all players that have lost exactly one match.
The values in the two lists should be returned in increasing order.
E.g.:
[[1,3],[2,3],[3,6],[5,6],[5,7],[4,5],[4,8],[4,9],[10,4],[10,9]]
=>[[1,2,10],[4,5,7,8]]
๐ฌ Thought Process - Two Hash Maps
- We need to find players who have lost 0 matches and players who have lost exactly 1 match.
- We can use two hash maps:
countWinsByPlayers
to keep track of count of wins of players andcountLossByPlayers
to keep track of the count of losses of players. - Basically for
match[i] = [winner, loser]
thecountWinsByPlayers
would contain the count of wins from playerwinner
andcountLossByPlayers
would contain the count of losses from playerloser
. - We iterate through every match in the matches array and
- If winner
j
wins a match we will increment the count of games won byj
in thecountWinsByPlayers
map. - If loser
k
loses a match, we will increment the count of games won byk
in thecountLossByPlayers
map.
- If winner
- Once all the matches have been processed, we can iterate over both the maps individually.
- For every key
player
in thecountWinsByPlayers
, ifplayer
has not lost any match, i.e.countLossByPlayers.get(player) == 0
, we can addplayer
to the list of players who has lost 0 matches. - For every key,
player
, in thecountLossByPlayers
, if the count of loses ofplayer
is exactly 1, i.e.countLossByPlayers.get(player) == 1
, we can addplayer
to the list of players with exactly 1 loss. - Finally, we sort both the winners and losers list individually and return the answer.
๐ฉ๐ฝโ๐ป Solution - Two Hash Maps
Below is the code for the approach using Hash Map
class Solution { public List<List<Integer>> findWinners(int[][] matches) { Map<Integer, Integer> countWinsByPlayer = new HashMap<>(); Map<Integer, Integer> countLossByPlayer = new HashMap<>(); for(int[] match: matches) { int winner = match[0]; int loser = match[1]; if(!countWinsByPlayer.containsKey(winner)) { countWinsByPlayer.put(winner, 0); } int winCount = countWinsByPlayer.get(winner); countWinsByPlayer.put(winner, winCount + 1); if(!countLossByPlayer.containsKey(loser)) { countLossByPlayer.put(loser, 0); } int loseCount = countLossByPlayer.get(loser); countLossByPlayer.put(loser, loseCount + 1); } List<List<Integer>> answer = new ArrayList(); answer.add(new ArrayList()); answer.add(new ArrayList()); for(int winner: countWinsByPlayer.keySet()) { if(!countLossByPlayer.containsKey(winner)) { answer.get(0).add(winner); } } for(int loser: countLossByPlayer.keySet()) { if(countLossByPlayer.get(loser) == 1) { answer.get(1).add(loser); } } Collections.sort(answer.get(0)); Collections.sort(answer.get(1)); return answer; } }
Time Complexity: O(n log n) - n = number of players - Sorting of both the list of answers and losers takes O(n log n) in the worst case Space Complexity: O(n) - n = number of players
๐ฌ Thought Process - Single Hash Map
- Using two separate maps is not a very elegant solution. We can instead use a single map to solve this problem.
- Instead of saving both wins and losses separately, we can use a single map that stores losses.
- For a player who wins the match:
- we update the map to hold
<player, 0>
indicating that the player has lost 0 matches => If the player has been processed for the first time (i.e. if the player has not lost any match before) - we don't update the entry if the
player
has already won or lost a match before.
- we update the map to hold
- For a player who loses the match:
- we update the map to hold
<player, 1>
indicating that the player has lost 1 match => If the player has not lost or won any matches before - we update the entry if the
player
already has lost or won the match before by incrementing by 1
- we update the map to hold
๐ฉ๐ฝโ๐ป Solution - Single Hash Map
Below is the code for the approach using a single Hash Map
class Solution { public List<List<Integer>> findWinners(int[][] matches) { Map<Integer, Integer> lossCount = new HashMap(); for(int[] match: matches) { int winner = match[0]; int loser = match[1]; if(!lossCount.containsKey(loser)) { lossCount.put(loser, 0); } int count = lossCount.get(loser); lossCount.put(loser, count + 1); if(!lossCount.containsKey(winner)) { lossCount.put(winner, 0); } } List<List<Integer>> answer = new ArrayList(); List<Integer> winners = new ArrayList(), losers = new ArrayList(); for(int player: lossCount.keySet()) { if(lossCount.get(player) == 0) { winners.add(player); } else if(lossCount.get(player) == 1) { losers.add(player); } } Collections.sort(winners); Collections.sort(losers); answer.add(winners); answer.add(losers); return answer; } }
Time Complexity: O(n log n) - n = number of players - At most we can have n players and sorting takes O(n * log n) Space Complexity: O(n) - n = number of players
๐ฌ Thought Process - Counting Sort
- From the constraints we can see that the range of matches is the same as the range of players. Hence we can use count sort such that
array[i]
holds the information of the ith element. - We use a similar strategy as done in the previous approach and keep track of losses of every player in an array of the same size as the range (10,000).
- Hence,
player[i]
would hold the count of losses of ith player.- If
player[i] == 0
: 0 matches lost - If
player[i] == 1
: 1 match lost - If
player[i] > 1
: more that 1 matches lost
- If
- Initially, we will fill the array with -1 and whenever a player
i
has won a match for the first time, we'll set the value atith
index to0
. - If a player
i
has neither lost or won any match and loses for the first time, we initialise the value at indexi
to 1. - If a player
i
has won or lost a match previously, we will simply increment the value at indexi
. - In the end, we will iterate from 0th to 10,000 index and check for the winners
player[i] == 0
and losersplayer[i] == 1
and add them into two separate lists. - We will have no need of sorting here since we are iterating from left to right (lower to higher index).
๐ฉ๐ฝโ๐ป Solution - Counting Sort
Below is the code for the approach using counting sort
class Solution { public List<List<Integer>> findWinners(int[][] matches) { int[] matchCount = new int[100001]; Arrays.fill(matchCount, -1); for(int[] match: matches) { int winner = match[0]; int loser = match[1]; int winnerCount = matchCount[winner]; if(winnerCount == -1) { matchCount[winner] = 0; } int loserCount = matchCount[loser]; matchCount[loser] += (loserCount == -1) ? 2 : 1; } List<Integer> winners = new ArrayList(); List<Integer> losers = new ArrayList(); for(int player = 1; player <= 100000; player++) { int count = matchCount[player]; if(count == 0) { winners.add(player); } else if(count == 1) { losers.add(player); } } List<List<Integer>> answer = new ArrayList(); answer.add(winners); answer.add(losers); return answer; } }
Time Complexity: O(n) - n = number of players - At most we can have n players Space Complexity: O(n) - n = number of players
- You can find the link to the GitHub repo for this question here: 2225. Find Players With Zero or One Losses.
Conclusion
That's a wrap for today's problem. If you liked my explanation then please do drop a like/ comment. Also, please correct me if I've made any mistakes or if you want me to improve something!
Thank you for reading!