Skip to content

Commit 9875648

Browse files
feat: add TopKFrequentWords with deterministic tie-breaking (#7298) (#7297)
* feat: add TopKFrequentWords with deterministic tie-breaking * style: format TopKFrequentWords files with clang-format
1 parent 4b04ad4 commit 9875648

File tree

2 files changed

+90
-0
lines changed

2 files changed

+90
-0
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.thealgorithms.strings;
2+
3+
import java.util.ArrayList;
4+
import java.util.Comparator;
5+
import java.util.HashMap;
6+
import java.util.List;
7+
import java.util.Map;
8+
9+
/**
10+
* Utility class to find the top-k most frequent words.
11+
*
12+
* <p>Words are ranked by frequency in descending order. For equal frequencies,
13+
* words are ranked in lexicographical ascending order.
14+
*
15+
* <p>Reference:
16+
* https://en.wikipedia.org/wiki/Top-k_problem
17+
*
18+
*/
19+
public final class TopKFrequentWords {
20+
private TopKFrequentWords() {
21+
}
22+
23+
/**
24+
* Finds the k most frequent words.
25+
*
26+
* @param words input array of words
27+
* @param k number of words to return
28+
* @return list of top-k words ordered by frequency then lexicographical order
29+
* @throws IllegalArgumentException if words is null, k is negative, or words contains null
30+
*/
31+
public static List<String> findTopKFrequentWords(String[] words, int k) {
32+
if (words == null) {
33+
throw new IllegalArgumentException("Input words array cannot be null.");
34+
}
35+
if (k < 0) {
36+
throw new IllegalArgumentException("k cannot be negative.");
37+
}
38+
if (k == 0 || words.length == 0) {
39+
return List.of();
40+
}
41+
42+
Map<String, Integer> frequency = new HashMap<>();
43+
for (String word : words) {
44+
if (word == null) {
45+
throw new IllegalArgumentException("Input words cannot contain null values.");
46+
}
47+
frequency.put(word, frequency.getOrDefault(word, 0) + 1);
48+
}
49+
50+
List<String> candidates = new ArrayList<>(frequency.keySet());
51+
candidates.sort(Comparator.<String>comparingInt(frequency::get).reversed().thenComparing(Comparator.naturalOrder()));
52+
53+
int limit = Math.min(k, candidates.size());
54+
return new ArrayList<>(candidates.subList(0, limit));
55+
}
56+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.thealgorithms.strings;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
6+
import java.util.List;
7+
import java.util.stream.Stream;
8+
import org.junit.jupiter.params.ParameterizedTest;
9+
import org.junit.jupiter.params.provider.Arguments;
10+
import org.junit.jupiter.params.provider.MethodSource;
11+
12+
class TopKFrequentWordsTest {
13+
14+
@ParameterizedTest
15+
@MethodSource("validTestCases")
16+
void testFindTopKFrequentWords(String[] words, int k, List<String> expected) {
17+
assertEquals(expected, TopKFrequentWords.findTopKFrequentWords(words, k));
18+
}
19+
20+
static Stream<Arguments> validTestCases() {
21+
return Stream.of(Arguments.of(new String[] {"i", "love", "leetcode", "i", "love", "coding"}, 2, List.of("i", "love")), Arguments.of(new String[] {"the", "day", "is", "sunny", "the", "the", "the", "sunny", "is", "is"}, 4, List.of("the", "is", "sunny", "day")),
22+
Arguments.of(new String[] {"bbb", "aaa", "bbb", "aaa", "ccc"}, 2, List.of("aaa", "bbb")), Arguments.of(new String[] {"one", "two", "three"}, 10, List.of("one", "three", "two")), Arguments.of(new String[] {}, 3, List.of()), Arguments.of(new String[] {"x", "x", "y"}, 0, List.of()));
23+
}
24+
25+
@ParameterizedTest
26+
@MethodSource("invalidTestCases")
27+
void testFindTopKFrequentWordsInvalidInput(String[] words, int k) {
28+
assertThrows(IllegalArgumentException.class, () -> TopKFrequentWords.findTopKFrequentWords(words, k));
29+
}
30+
31+
static Stream<Arguments> invalidTestCases() {
32+
return Stream.of(Arguments.of((String[]) null, 1), Arguments.of(new String[] {"a", null, "b"}, 2), Arguments.of(new String[] {"a"}, -1));
33+
}
34+
}

0 commit comments

Comments
 (0)