|
| 1 | +package LeetCodeJava.Heap; |
| 2 | + |
| 3 | +// https://leetcode.com/problems/hand-of-straights/description/ |
| 4 | + |
| 5 | +import java.util.*; |
| 6 | + |
| 7 | +/** |
| 8 | + * 846. Hand of Straights |
| 9 | + * Medium |
| 10 | + * Topics |
| 11 | + * Companies |
| 12 | + * Alice has some number of cards and she wants to rearrange the cards into groups so that each group is of size groupSize, and consists of groupSize consecutive cards. |
| 13 | + * |
| 14 | + * Given an integer array hand where hand[i] is the value written on the ith card and an integer groupSize, return true if she can rearrange the cards, or false otherwise. |
| 15 | + * |
| 16 | + * |
| 17 | + * |
| 18 | + * Example 1: |
| 19 | + * |
| 20 | + * Input: hand = [1,2,3,6,2,3,4,7,8], groupSize = 3 |
| 21 | + * Output: true |
| 22 | + * Explanation: Alice's hand can be rearranged as [1,2,3],[2,3,4],[6,7,8] |
| 23 | + * Example 2: |
| 24 | + * |
| 25 | + * Input: hand = [1,2,3,4,5], groupSize = 4 |
| 26 | + * Output: false |
| 27 | + * Explanation: Alice's hand can not be rearranged into groups of 4. |
| 28 | + * |
| 29 | + * |
| 30 | + * |
| 31 | + * Constraints: |
| 32 | + * |
| 33 | + * 1 <= hand.length <= 104 |
| 34 | + * 0 <= hand[i] <= 109 |
| 35 | + * 1 <= groupSize <= hand.length |
| 36 | + * |
| 37 | + * |
| 38 | + * Note: This question is the same as 1296: https://leetcode.com/problems/divide-array-in-sets-of-k-consecutive-numbers/ |
| 39 | + * |
| 40 | + */ |
| 41 | +public class HandOfStraights { |
| 42 | + |
| 43 | + // V0 |
| 44 | +// public boolean isNStraightHand(int[] hand, int groupSize) { |
| 45 | +// |
| 46 | +// } |
| 47 | + |
| 48 | + // V0' |
| 49 | + // TODO : fix below |
| 50 | + // NOTE !!! PriorityQueue in Java by default is a min-heap (not a max-heap). So, the negation and handling logic need to be fixed. |
| 51 | +// public boolean isNStraightHand(int[] hand, int groupSize) { |
| 52 | +// |
| 53 | +// if (hand.length % groupSize != 0){ |
| 54 | +// return false; |
| 55 | +// } |
| 56 | +// |
| 57 | +// if (groupSize==1){ |
| 58 | +// return true; |
| 59 | +// } |
| 60 | +// |
| 61 | +// if (hand.length == groupSize){ |
| 62 | +// // sort |
| 63 | +// Arrays.sort(hand); |
| 64 | +// //int min = hand[0]; |
| 65 | +// for (int k = 0; k < hand.length-1; k++){ |
| 66 | +// if (hand[k]+1 != hand[k+1]){ |
| 67 | +// return false; |
| 68 | +// } |
| 69 | +// } |
| 70 | +// return true; |
| 71 | +// } |
| 72 | +// |
| 73 | +// |
| 74 | +// // PQ (max heap???) |
| 75 | +// PriorityQueue<Integer> pq = new PriorityQueue(); |
| 76 | +// for (int i = 0; i < hand.length; i++){ |
| 77 | +// // NOTE !! we put "-1 * i" |
| 78 | +// // so can create a "small" PQ |
| 79 | +// pq.add(-1 * hand[i]); |
| 80 | +// } |
| 81 | +// |
| 82 | +// System.out.println(">>> pq = " + pq); |
| 83 | +// |
| 84 | +// // check |
| 85 | +// while (!pq.isEmpty()){ |
| 86 | +// // get cur min element |
| 87 | +// int cnt = 0; |
| 88 | +// int min = pq.poll(); |
| 89 | +// Queue<Integer> tmpQ = new LinkedList<>(); |
| 90 | +// if (!pq.contains(min+1) || !pq.contains(min+2)){ |
| 91 | +// return false; |
| 92 | +// } |
| 93 | +// // till collect groupSize count of element |
| 94 | +// for (int j = 0; j < groupSize && !pq.isEmpty(); j++){ |
| 95 | +// int cur = pq.poll(); |
| 96 | +// // found consecutive element |
| 97 | +// if (cur == min+1){ |
| 98 | +// min += 1; |
| 99 | +// // if not found, add to tmp queue |
| 100 | +// }else{ |
| 101 | +// tmpQ.add(cur); |
| 102 | +// } |
| 103 | +// } |
| 104 | +// |
| 105 | +// // put tmp element back to pq |
| 106 | +// while (!tmpQ.isEmpty()){ |
| 107 | +// pq.add(tmpQ.poll()); |
| 108 | +// } |
| 109 | +// |
| 110 | +// // re-ordering PQ ??? needed ? |
| 111 | +// } |
| 112 | +// |
| 113 | +// return true; |
| 114 | +// } |
| 115 | + |
| 116 | + |
| 117 | + // V1-1 |
| 118 | + // IDEA: TreeMap (gpt) |
| 119 | + public boolean isNStraightHand_1_1(int[] hand, int groupSize) { |
| 120 | + // If the total number of cards is not divisible by groupSize, return false |
| 121 | + if (hand.length % groupSize != 0) { |
| 122 | + return false; |
| 123 | + } |
| 124 | + |
| 125 | + // TreeMap to count occurrences of each card |
| 126 | + /** |
| 127 | + * TreeMap for Counting: We use a TreeMap to count occurrences of each card. |
| 128 | + * TreeMap maintains the natural order of the keys, making it easier |
| 129 | + * to process the smallest card and form consecutive groups. |
| 130 | + */ |
| 131 | + TreeMap<Integer, Integer> cardCountMap = new TreeMap<>(); |
| 132 | + for (int card : hand) { |
| 133 | + cardCountMap.put(card, cardCountMap.getOrDefault(card, 0) + 1); |
| 134 | + } |
| 135 | + |
| 136 | + // Form groups starting with the smallest card in the map |
| 137 | + while (!cardCountMap.isEmpty()) { |
| 138 | + /** |
| 139 | + * Group Formation: |
| 140 | + * • For each smallest card (retrieved using firstKey()), |
| 141 | + * we try to form a group of size groupSize by checking consecutive cards. |
| 142 | + * |
| 143 | + * • For each card in the group, we reduce its count. |
| 144 | + * If the count becomes zero, we remove that card from the TreeMap. |
| 145 | + */ |
| 146 | + int firstCard = cardCountMap.firstKey(); // Get the smallest card |
| 147 | + |
| 148 | + // Try to form a group starting from the smallest card |
| 149 | + for (int i = 0; i < groupSize; i++) { |
| 150 | + int currentCard = firstCard + i; |
| 151 | + if (!cardCountMap.containsKey(currentCard)) { |
| 152 | + // Early Return: If at any point we cannot form a group (because a required card is missing), we return false. |
| 153 | + return false; // Cannot form a consecutive group |
| 154 | + } |
| 155 | + |
| 156 | + // Decrease the count of the current card |
| 157 | + int count = cardCountMap.get(currentCard); |
| 158 | + if (count == 1) { |
| 159 | + cardCountMap.remove(currentCard); // Remove the card if the count is zero |
| 160 | + } else { |
| 161 | + cardCountMap.put(currentCard, count - 1); |
| 162 | + } |
| 163 | + } |
| 164 | + } |
| 165 | + |
| 166 | + return true; |
| 167 | + } |
| 168 | + |
| 169 | + // V1-2 |
| 170 | + // IDEA: HashMap (gpt) |
| 171 | + public boolean isNStraightHand_1_2(int[] hand, int groupSize) { |
| 172 | + // If the total number of cards is not divisible by groupSize, return false |
| 173 | + if (hand.length % groupSize != 0) { |
| 174 | + return false; |
| 175 | + } |
| 176 | + |
| 177 | + // HashMap to count occurrences of each card |
| 178 | + HashMap<Integer, Integer> cardCountMap = new HashMap<>(); |
| 179 | + for (int card : hand) { |
| 180 | + cardCountMap.put(card, cardCountMap.getOrDefault(card, 0) + 1); |
| 181 | + } |
| 182 | + |
| 183 | + // Sort the unique cards |
| 184 | + int[] uniqueCards = cardCountMap.keySet().stream().mapToInt(Integer::intValue).toArray(); |
| 185 | + Arrays.sort(uniqueCards); |
| 186 | + |
| 187 | + // Try to form groups starting from the smallest card |
| 188 | + for (int card : uniqueCards) { |
| 189 | + int count = cardCountMap.get(card); |
| 190 | + if (count > 0) { |
| 191 | + // Try to form a group of size `groupSize` starting from `card` |
| 192 | + for (int i = 0; i < groupSize; i++) { |
| 193 | + int currentCard = card + i; |
| 194 | + if (cardCountMap.getOrDefault(currentCard, 0) < count) { |
| 195 | + return false; // Not enough cards to form the group |
| 196 | + } |
| 197 | + cardCountMap.put(currentCard, cardCountMap.get(currentCard) - count); |
| 198 | + } |
| 199 | + } |
| 200 | + } |
| 201 | + |
| 202 | + return true; |
| 203 | + } |
| 204 | + |
| 205 | + // V2-1 |
| 206 | + // IDEA : Using Map |
| 207 | + // https://leetcode.com/problems/hand-of-straights/editorial/ |
| 208 | + public boolean isNStraightHand_2_1(int[] hand, int groupSize) { |
| 209 | + int handSize = hand.length; |
| 210 | + if (handSize % groupSize != 0) { |
| 211 | + return false; |
| 212 | + } |
| 213 | + |
| 214 | + // TreeMap to store the count of each card value |
| 215 | + Map<Integer, Integer> cardCount = new TreeMap<>(); |
| 216 | + for (int i = 0; i < handSize; i++) { |
| 217 | + cardCount.put(hand[i], cardCount.getOrDefault(hand[i], 0) + 1); |
| 218 | + } |
| 219 | + |
| 220 | + // Process the cards until the map is empty |
| 221 | + while (cardCount.size() > 0) { |
| 222 | + // Get the smallest card value |
| 223 | + int current_card = cardCount.entrySet().iterator().next().getKey(); |
| 224 | + // Check each consecutive sequence of groupSize cards |
| 225 | + for (int i = 0; i < groupSize; i++) { |
| 226 | + // If a card is missing or has exhausted its occurrences |
| 227 | + if (!cardCount.containsKey(current_card + i)) return false; |
| 228 | + cardCount.put( |
| 229 | + current_card + i, |
| 230 | + cardCount.get(current_card + i) - 1 |
| 231 | + ); |
| 232 | + // Remove the card value if its occurrences are exhausted |
| 233 | + if (cardCount.get(current_card + i) == 0) cardCount.remove( |
| 234 | + current_card + i |
| 235 | + ); |
| 236 | + } |
| 237 | + } |
| 238 | + |
| 239 | + return true; |
| 240 | + } |
| 241 | + |
| 242 | + // V2-2 |
| 243 | + // IDEA : TreeMap + Queue (Optimal with hashMap) |
| 244 | + // https://leetcode.com/problems/hand-of-straights/editorial/ |
| 245 | + public boolean isNStraightHand_2_2(int[] hand, int groupSize) { |
| 246 | + // Map to store the count of each card value |
| 247 | + Map<Integer, Integer> cardCount = new TreeMap<>(); |
| 248 | + |
| 249 | + for (int card : hand) { |
| 250 | + cardCount.put(card, cardCount.getOrDefault(card, 0) + 1); |
| 251 | + } |
| 252 | + |
| 253 | + // Queue to keep track of the number of new groups |
| 254 | + // starting with each card value |
| 255 | + Queue<Integer> groupStartQueue = new LinkedList<>(); |
| 256 | + int lastCard = -1, currentOpenGroups = 0; |
| 257 | + for (Map.Entry<Integer, Integer> entry : cardCount.entrySet()) { |
| 258 | + int currentCard = entry.getKey(); |
| 259 | + // Check if there are any discrepancies in the sequence |
| 260 | + // or more groups are required than available cards |
| 261 | + if ( |
| 262 | + (currentOpenGroups > 0 && currentCard > lastCard + 1) || |
| 263 | + currentOpenGroups > cardCount.get(currentCard) |
| 264 | + ) { |
| 265 | + return false; |
| 266 | + } |
| 267 | + // Calculate the number of new groups starting with the current card |
| 268 | + groupStartQueue.offer( |
| 269 | + cardCount.get(currentCard) - currentOpenGroups |
| 270 | + ); |
| 271 | + lastCard = currentCard; |
| 272 | + currentOpenGroups = cardCount.get(currentCard); |
| 273 | + // Maintain the queue size to be equal to groupSize |
| 274 | + if (groupStartQueue.size() == groupSize) { |
| 275 | + currentOpenGroups -= groupStartQueue.poll(); |
| 276 | + } |
| 277 | + } |
| 278 | + |
| 279 | + // All groups should be completed by the end |
| 280 | + return currentOpenGroups == 0; |
| 281 | + } |
| 282 | + |
| 283 | +} |
0 commit comments