算法思路
- 分词:使用正则表达式或特定的分词规则,将长文本分割成一个个单词。
- 词频统计:使用
HashMap
来存储每个单词及其出现的次数。遍历分词结果,若单词已存在于HashMap
中,则将其对应的值加1;否则,将单词作为键,值设为1加入HashMap
。
- 按照词频降序输出:将
HashMap
中的键值对转换为List
,然后使用Collections.sort
方法根据词频进行降序排序。
- 多线程处理:为了在多线程环境下实现线程安全,使用
StringBuffer
类。因为StringBuffer
的方法是同步的,在多线程环境下可以保证数据的一致性。每个线程处理文本的一部分,分词和词频统计操作在线程内完成,最后合并各个线程的统计结果。
数据结构设计
HashMap<String, Integer>
:用于存储单词及其出现的次数。
List<Map.Entry<String, Integer>>
:用于存储HashMap
中的键值对,方便进行排序。
利用StringBuffer
确保线程安全
- 在多线程环境下,
StringBuffer
的方法(如append
等)是同步的。在处理文本时,每个线程可以使用自己的StringBuffer
实例来处理文本片段,避免线程安全问题。例如,在分词过程中,每个线程将其负责的文本片段放入StringBuffer
,然后进行分词操作。
- 对于共享数据(如词频统计的
HashMap
),在更新时需要进行同步操作。可以使用synchronized
关键字或者ConcurrentHashMap
(更高效的线程安全哈希表)来保证线程安全。
示例代码(Java)
import java.util.*;
public class StringProcessing {
public static void main(String[] args) {
String longText = "这是一段示例文本,这是一段示例文本,示例文本";
int numThreads = 2;
List<Thread> threads = new ArrayList<>();
Map<String, Integer> wordFreqMap = new HashMap<>();
int length = longText.length();
int partLength = length / numThreads;
for (int i = 0; i < numThreads; i++) {
int start = i * partLength;
int end = (i == numThreads - 1)? length : (i + 1) * partLength;
String partText = longText.substring(start, end);
Thread thread = new Thread(() -> {
StringBuffer buffer = new StringBuffer(partText);
String[] words = buffer.toString().split("\\W+");
Map<String, Integer> localFreqMap = new HashMap<>();
for (String word : words) {
localFreqMap.put(word, localFreqMap.getOrDefault(word, 0) + 1);
}
synchronized (wordFreqMap) {
localFreqMap.forEach((word, freq) -> {
wordFreqMap.put(word, wordFreqMap.getOrDefault(word, 0) + freq);
});
}
});
threads.add(thread);
thread.start();
}
threads.forEach(thread -> {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
List<Map.Entry<String, Integer>> entryList = new ArrayList<>(wordFreqMap.entrySet());
entryList.sort((e1, e2) -> e2.getValue().compareTo(e1.getValue()));
entryList.forEach(entry -> System.out.println(entry.getKey() + ": " + entry.getValue()));
}
}
时间复杂度分析
- 分词:假设文本长度为
n
,分词操作(使用split
方法)的时间复杂度为$O(n)$,因为需要遍历整个文本。
- 词频统计:遍历分词结果,假设单词数量为
m
,每次在HashMap
中查找和更新的时间复杂度平均为$O(1)$,所以总的时间复杂度为$O(m)$。
- 排序:将
HashMap
转换为List
并排序,假设List
的大小为m
,使用Collections.sort
(通常基于快速排序或归并排序)的时间复杂度为$O(m log m)$。
- 整体时间复杂度:由于
m
通常小于n
,主要时间消耗在分词和排序上,整体时间复杂度为$O(n + m log m)$。
空间复杂度分析
HashMap
:存储单词及其词频,假设单词数量为m
,空间复杂度为$O(m)$。
List
:存储排序后的键值对,空间复杂度为$O(m)$。
- 其他临时变量:如
StringBuffer
等,假设其最大长度为k
(k <= n
),空间复杂度为$O(k)$。
- 整体空间复杂度:$O(m + k)$,在最坏情况下,
m
和k
都接近n
,空间复杂度为$O(n)$。