2225. Find Players With Zero or One Losses

2225. Find Players With Zero or One Losses

Problem Solving - Day 58

ยท

6 min read

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 player winner defeated player loser 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 and countLossByPlayers to keep track of the count of losses of players.
  • Basically for match[i] = [winner, loser] the countWinsByPlayers would contain the count of wins from player winner and countLossByPlayers would contain the count of losses from player loser.
  • We iterate through every match in the matches array and
    • If winner j wins a match we will increment the count of games won by j in the countWinsByPlayers map.
    • If loser k loses a match, we will increment the count of games won by k in the countLossByPlayers map.
  • Once all the matches have been processed, we can iterate over both the maps individually.
  • For every key player in the countWinsByPlayers, if player has not lost any match, i.e. countLossByPlayers.get(player) == 0, we can add player to the list of players who has lost 0 matches.
  • For every key, player, in the countLossByPlayers, if the count of loses of player is exactly 1, i.e. countLossByPlayers.get(player) == 1, we can add player 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.
  • 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

๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿ’ป 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
  • 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 at ith index to 0.
  • If a player i has neither lost or won any match and loses for the first time, we initialise the value at index i to 1.
  • If a player i has won or lost a match previously, we will simply increment the value at index i.
  • In the end, we will iterate from 0th to 10,000 index and check for the winners player[i] == 0 and losers player[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
    


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!