Most efficient data structure for storing an alphabetically ordered word list
我的程序将读入一段单词(存储在文本文件中)。然后需要执行以下操作:
- 打印出所有单词的列表(按字母顺序排列)。对于每个单词,打印频率计数(单词出现在整个段落中的次数)和单词出现的行号(不需要订购)。如果一个单词出现在一行多次,则行号不需要存储两次(该单词的频率计数仍将更新)
- 显示从最频繁到最不频繁排序的单词列表。
- 用户将输入特定单词。如果找到该单词,则打印出其频率计数。
限制:我不能使用
编写这个并不难,但我无法弄清楚什么是最有效的实现,因为数据大小可能是维基百科文章中的几个段落。这是我现在的想法:
- 有一个Word课程。此Word类将包含返回单词的频率计数和单词出现的行(以及其他相关数据)的方法。
- 该段落将存储在文本文件中。程序将逐行读取数据。将该行拆分为一个数组并逐个读入。
- 当从文本文件中读取单词时,将单词放入某种结构中。如果结构不包含单词,请创建一个新的单词对象。
-
如果结构已包含该单词,请更新该单词的频率计数器。
-
我还会有一个
int 来记录行号。这些行号将相应更新。
-
我还会有一个
这有点不完整,但这正是我现在想的。整个'Word'类可能也完全没必要。
首先,您可以创建一个类,用于保存事件和行号(以及单词)的数据。这个类可以实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | public class WordOccurrence implements Comparable<WordOccurrence> { private final String word; private int totalCount = 0; private Set<Integer> lineNumbers = new TreeSet<>(); public WordOccurrence(String word, int firstLineNumber) { this.word = word; addOccurrence(firstLineNumber); } public final void addOccurrence(int lineNumber) { totalCount++; lineNumbers.add(lineNumber); } @Override public int compareTo(WordOccurrence o) { return totalCount - o.totalCount; } @Override public String toString() { StringBuilder lineNumberInfo = new StringBuilder("["); for (int line : lineNumbers) { if (lineNumberInfo.length() > 1) { lineNumberInfo.append(","); } lineNumberInfo.append(line); } lineNumberInfo.append("]"); return word +", occurences:" + totalCount +", on rows" + lineNumberInfo.toString(); } } |
从文件中读取单词时,返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public TreeMap<String, WordOccurrence> countOccurrences(String filePath) throws IOException { TreeMap<String, WordOccurrence> words = new TreeMap<>(); File file = new File(filePath); BufferedReader reader = new BufferedReader(new InputStreamReader( new FileInputStream(file))); String line = null; int lineNumber = 0; while ((line = reader.readLine()) != null) { // remove punctuation and normalize to lower-case line = line.replaceAll("\\p{P}","").toLowerCase(); lineNumber++; String[] tokens = line.split("\\s+"); for (String token : tokens) { if (words.containsKey(token)) { words.get(token).addOccurrence(lineNumber); } else { words.put(token, new WordOccurrence(token, lineNumber)); } } } return words; } |
使用上面的代码按字母顺序显示事件非常简单
1 2 3 4 |
如果您不能使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | public static void displayInOrderOfOccurrence( Map<String, WordOccurrence> words) { List<WordOccurrence> orderedByOccurrence = new ArrayList<>(); // sort for (Map.Entry<String, WordOccurrence> entry : words.entrySet()) { WordOccurrence wo = entry.getValue(); // initialize the list on the first round if (orderedByOccurrence.isEmpty()) { orderedByOccurrence.add(wo); } else { for (int i = 0; i < orderedByOccurrence.size(); i++) { if (wo.compareTo(orderedByOccurrence.get(i)) > 0) { orderedByOccurrence.add(i, wo); break; } else if (i == orderedByOccurrence.size() - 1) { orderedByOccurrence.add(wo); break; } } } } // display for (WordOccurrence wo : orderedByOccurence) { System.out.println(wo); } } |
使用以下测试数据运行上述代码:
1 2 3 | Potato; orange. Banana; apple, apple; potato. Potato. |
将产生此输出:
1 2 3 4 5 6 7 8 9 | apple, occurrences: 2, on rows [2] banana, occurrences: 1, on rows [2] orange, occurrences: 1, on rows [1] potato, occurrences: 3, on rows [1, 2, 3] potato, occurrences: 3, on rows [1, 2, 3] apple, occurrences: 2, on rows [2] banana, occurrences: 1, on rows [2] orange, occurrences: 1, on rows [1] |
您可以使用简单的
鉴于单词很短(即你会找到普通文本),查找应该是O(1)。如果您期望大量不成功的查找(大量搜索不存在的单词),您可以使用Bloom过滤器进行预过滤。
我将从一个简单的实现开始,并在需要时进一步优化(直接解析流,而不是使用分隔符拆分每一行并重复)。
你可以使用TreeMap它非常适合获取订购的数据。使用您的单词作为键,频率作为值。例如,让以下是你的段落
Java是优秀的语言Java是面向对象的
所以我将执行以下操作以存储每个单词及其频率
1 2 3 4 5 6 7 8 9 10 11 12 |
希望这会对你有所帮助
你可以有这样的结构:
https://gist.github.com/jeorfevre/946ede55ad93cc811cf8
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | /** * * @author Jean-Emmanuel [email protected] * */ public class WordsIndex{ HashMap<String, Word> words = new HashMap<String, Word>(); public static void put(String word, int line, int paragraph){ word=word.toLowerCase(); if(words.containsKey(word)){ Word w=words.get(word); w.count++; }else{ //new word Word w = new Word(); w.count=1; w.line=line; w.paragraph=paragraph; w.word=word; words.put(word, w); } } } public class Word{ String word; int count; int line; int paragraph; } |
请享用