题目:1000万字符串,其中有些是重复的,需要把重复的全部去掉,保留没有重复的字符串。请怎么设计和实现?
大数据的字符串处理我一般想到了trie树和hashmap,jdk里有hashmap的实现,所以想先用hashmap来试试效果,在用hashmap来测试前先编个小代码,用来生成1000万的字符串,使用随机函数来选择字符:
//生成sum个单词,并输入到word.txt文件中去。
public static void produceWord(int sum){
char[] c = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o'
,'p','q','r','s','t','u','v','w','x','y','z'};
Random r = new Random();
int length =0;
int count =0;
StringBuffer sb = new StringBuffer("");
while(count<sum){
length = r.nextInt(10)+1;
for(int i=0;i<length;i++){
sb.append(c[r.nextInt(26)]);
}
FileUtils.writeWord(new String(sb),FileUtils.getFile("word.txt"));//这是我写的一个工具类,直接写入带刺到word.txt文件中
sb.delete(0, sb.length());
count++;
}
}
我用上面的函数生成100万个单词并写入到文件中花了670秒,也就是10分钟,因为每个单词的最大长度是10,while循环最坏会有1000万次的执行(1000万的字符串太久了,所以不想试了),生成的word.txt文件中一行对应一个单词,下面来用jdk自带的hashmap来排除100万个单词里面的重复单词,即题目的要求:
public static void main(String[] args) {
long time = 0;
time = TimeUtils.printTime();//这是我的工具类,直接输出当前的时间
// produceWord(1000000);
try {
BufferedReader br = new BufferedReader(new FileReader(FileUtils.getFile("word.txt")));
FileWriter fw = new FileWriter(FileUtils.getFile("treeWord.txt"),true);
HashMap<String, Integer> hm = new HashMap<String, Integer>();
String s = null;
while((s=br.readLine())!=null){
if(!hm.containsKey(s)){ //如果hm没有包含s这个单词,则把s加入到hm,同时写入文件treeWord.txt中
hm.put(s, 1);
//输出到文件
fw.write(s);
fw.write("\r\n");
fw.flush();
}
}
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
time = TimeUtils.printTime()-time;
TimeUtils.takeTime(time);//总共花费了多少秒
}
输出:
1333521185421
1333521201546
花费时间:16.125秒
上面用hashmap测试,从word.txt中读取100万个单词,并去重后写入treeWord文件,总共花了16秒的样子。看来还是比较快的。
接下来想试试用trie树来解决这个问题,jdk没有trie树,只好自己写一个了,先简单的介绍一下trie树,原理很简单,比如要表示i am a coder 这个句子的trie树,画个大致图就明白了:
当然,trie有很多变种,节点存储的东西也不一定一样,我这里节点存储了字符和一个数字,数字为0表示当前所在的节点没有单词,为1表示当前节点有单词,应该很好理解。
好了,接下来开始构建trie树,
static class treeNode {
treeNode[] node = null;
char value;
int count;// 单词计数,如果为0表示以此节点结尾的字符串,大于0则表示有count个以它结尾的字符串
int size;// 有几个孩子
................................
这是我的trie节点,node是他的子节点。节点会有一些很重要的操作:
// 循环添加字符串s到当前节点中,前提是已经判断了当前节点不存在以s开头的字符
public void addString(String s) {
char[] c = s.toCharArray();
treeNode tn = this;
int i = 0;
while (i < c.length) {
tn.addChild(new treeNode(c[i]));
tn = tn.node[tn.contain(c[i])];
i++;
}
tn.count++; //当前节点的字符串树加一
}
// 添加一个孩子,前提是已经判断了当前节点不存在这个子节点
public void addChild(treeNode child) {
if (getSize() == node.length) {
treeNode[] temp = new treeNode[node.length * 2];
System.arraycopy(node, 0, temp, 0, node.length);
node = temp;
}
node[size] = child;
size++;
}
// 如果有包含c的子节点则返回节点的位置
public int contain(char c) {
treeNode tn = new treeNode(c);
for (int i = 0; i < size; i++) {
if (node[i].isEqual(tn)) {
return i;
}
}
return -1;
}
注意,上面的代码都属于节点的操作,下面介绍在trie树中的一些操作:
//判断一个字典树中是否存在字符串str,只有返回值大于0才表示有这个字符串
private int contain(String str) {
if(str==null || str == "")
return -1;
treeNode nd = root;
int i =0;
int place;
while(i<str.length() && (place =nd.contain(str.charAt(i)))!=-1){
nd = nd.node[place];
i++;
}
if(i==str.length())
return nd.count;
else return -1;
}
// 在字典树中添加一个字符串 ,具体实现
private void addString(String str, treeNode node) {
treeNode tn = node;
int count;
int i = 0;
while (i < str.length()&& (count = tn.contain(str.charAt(i))) != -1 ) { //如果tn节点包含值为str[i]的子节点,则赋值这个子节点为新的tn,继 //续向下查找
tn = tn.node[count];
i++;
if(i==str.length()){
tn.count++;//所在树本来已经包含这个字符串,所以count数加一
}
}
if (i != str.length()) { //在str[i]后面的没有添加进trie树中
String s = str.substring(i);
tn.addString(s); // 直接调用节点的方法addString,插入str[i]后面的字符串
}
}
用word.txt的100万单词测试所写的trie树,看解决一开始说提到的问题,
public static void main(String[] args) {
long time = TimeUtils.printTime();
TrieTree t = new TrieTree(FileUtils.getFile("word.txt"));
time = TimeUtils.printTime()-time;
System.out.println("构建trie树花费了:");
TimeUtils.takeTime(time);
System.out.println("开始输出......");
time = TimeUtils.printTime();
t.printFile(FileUtils.getFile("treeWord.txt"));
time = TimeUtils.printTime()-time;
System.out.println("输出trie树到文件花费了...");
TimeUtils.takeTime(time);
}
输出:
1333521079359
1333521087187
构建trie树花费了:
花费时间:7.828秒
开始输出......
1333521087187
输出到treeWord.txt成功
1333521099843
输出trie树到文件花费了...
花费时间:12.656秒
加起来总共花费20秒的样子,跟上面haspmap差不多的速度,当然,我的机子运行起来比较卡,所以严格的来说,速度可能不是很准确,但大致还是知道了,hashmap的速度会要快一些,因为在添加一个字符串的时候,hashmap直接用哈希函数就能定位,然后选择是否put和写入文件,但是trie树需要在子节点中比较。
总结:1:这是根据“1000万字符串”而写的trie树,没有比较好的结构性,封装的不是很好。
2:个人觉得trie树对hashmap的优势是,在大量重复的单词中,trie树需要的内存会低一些,hashmap的优势是查找快一些。
欢迎拍砖!
以上只给了关键代码,需要完整代码留下邮箱.....
分享到:
相关推荐
在Java编程中,有时我们需要处理字符串,特别是去除其中的重复字符。这可能在处理用户输入、数据清洗或创建唯一标识时变得尤为重要。本教程将详细讲解三种不同的方法来实现这个功能,适合Java初学者作为学习参考资料...
java 字符串转16进制 16进制转字符串 将两个ASCII字符合成一个字节; java 字符串转16进制 16进制转字符串 将两个ASCII字符合成一个字节; java 字符串转16进制 16进制转字符串 将两个ASCII字符合成一个字节; java ...
这篇博客“Java实现计算字符串表达式”可能讲解了如何利用Java来处理这种问题,虽然具体的实现细节没有提供,但我们可以基于一般的方法和常用的库来探讨这个主题。 首先,计算字符串表达式的基本步骤通常包括以下几...
字符串加密pta:分别基于Java和Python的实现代码.zip 字符串加密pta:分别基于Java和Python的实现代码.zip 字符串加密pta:分别基于Java和Python的实现代码.zip 字符串加密pta:分别基于Java和Python的实现代码.zip ...
通过遍历主字符串和子串,如果可以顺利完成匹配,那么子串就在主字符串中;反之,不在。 4. 描述中的"2是检索出现几次": 如果我们要统计子串在主字符串中出现的次数,可以在找到一次匹配后,将主字符串的指针移动...
将`java.util.Date`类型的对象转换为字符串可以通过`SimpleDateFormat`类来实现。`SimpleDateFormat`是`java.text`包中的一个子类,它可以用来格式化和解析日期。下面是一个具体的例子: ```java import java.text....
在Java编程语言中,分割字符串是一项常见的操作,它允许我们将一个长字符串分解成多个子字符串,每个子字符串对应原字符串中的某个部分。这通常通过使用`split()`方法来实现,该方法是Java `String`类的一个成员。在...
本篇文章将深入探讨如何从字符串中提取括号内的内容,主要关注于基础的字符串操作、正则表达式以及如何利用这些工具来实现目标。 首先,我们要明白Java中的字符串是`String`类的对象,它提供了丰富的API用于字符串...
在Java编程语言中,字符串(String)是至关重要的数据类型,用于存储和操作文本。字符串类提供了丰富的API,使得处理字符串变得高效且灵活。本篇将深入探讨Java中的字符串、正则表达式及其在实际编程中的详细实例代码...
该算法的时间复杂度为 O(m+n),其中 m 和 n 分别是长字符串和短字符串的长度。在最好的情况下,时间复杂度为 O(n)。 结论 本文介绍了如何使用 Java 实现字符串匹配,并且提供了基于素数乘积的算法实现。该算法的...
java压缩字符串
去除重复字符串
java 字符串工具类 java 字符串工具类java 字符串工具类 java 字符串工具类java 字符串工具类 java 字符串工具类java 字符串工具类 java 字符串工具类java 字符串工具类 java 字符串工具类java 字符串工具类 java ...
java中 数组排序 26英文字母排序,去重复 去除 null 去除 空字符串 欢迎大家下载
通过对上述代码的详细分析,我们可以看到“字符串去重复”的基本实现思路和具体步骤。这种方法适用于大多数情况下的字符串去重需求,但需要注意的是,在实际应用中可能还需要考虑性能优化等问题。例如,当处理大量...
在Java编程语言中,分解字符串是一项常见的任务,它涉及到对字符串进行分析,将字符串分割成多个子字符串。这个过程通常被称为字符串分割。在Java中,我们主要使用`String`类提供的`split()`方法来实现这一功能。...
通过理解和实现LZ78算法,不仅可以学习到数据压缩的基本原理,还能深入理解字符串处理、字典数据结构和位操作等编程技巧。在实际应用中,LZ78算法常被用作其他更高效压缩算法的基础,例如LZW(Lempel-Ziv-Welch)...
去掉重复的字符串及在第一个字符串中删除在第二个字符串中出现的字符两个程序,vs2013已经验证
在Java编程语言中,字符串是极其重要且常用的数据类型,尤其对于初学者来说,理解和熟练掌握字符串的操作至关重要。本文将围绕“java字符串练习”这一主题,深入探讨如何解析字符串、逆序输出字符串以及处理特定格式...