“1000万字符串,去掉重复”的一些思考和java实现
2012-04-04 14:42
381 查看
题目:1000万字符串,其中有些是重复的,需要把重复的全部去掉,保留没有重复的字符串。请怎么设计和实现?
大数据的字符串处理我一般想到了trie树和hashmap,jdk里有hashmap的实现,所以想先用hashmap来试试效果,在用hashmap来测试前先编个小代码,用来生成1000万的字符串,使用随机函数来选择字符:
上面用hashmap测试,从word.txt中读取100万个单词,并去重后写入treeWord文件,总共花了16秒的样子。看来还是比较快的。
接下来想试试用trie树来解决这个问题,jdk没有trie树,只好自己写一个了,先简单的介绍一下trie树,原理很简单,比如要表示i am a coder 这个句子的trie树,画个大致图就明白了:
当然,trie有很多变种,节点存储的东西也不一定一样,我这里节点存储了字符和一个数字,数字为0表示当前所在的节点没有单词,为1表示当前节点有单词,应该很好理解。
好了,接下来开始构建trie树,
这是我的trie节点,node是他的子节点。节点会有一些很重要的操作:
注意,上面的代码都属于节点的操作,下面介绍在trie树中的一些操作:
用word.txt的100万单词测试所写的trie树,看解决一开始说提到的问题,
输出:
加起来总共花费20秒的样子,跟上面haspmap差不多的速度,当然,我的机子运行起来比较卡,所以严格的来说,速度可能不是很准确,但大致还是知道了,hashmap的速度会要快一些,因为在添加一个字符串的时候,hashmap直接用哈希函数就能定位,然后选择是否put和写入文件,但是trie树需要在子节点中比较。
总结:1:这是根据“1000万字符串”而写的trie树,没有比较好的结构性,封装的不是很好。
2:个人觉得trie树对hashmap的优势是,在大量重复的单词中,trie树需要的内存会低一些,hashmap的优势是查找快一些。
欢迎拍砖!
以上只给了关键代码,需要完整代码留下邮箱.....
大数据的字符串处理我一般想到了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的优势是查找快一些。
欢迎拍砖!
以上只给了关键代码,需要完整代码留下邮箱.....
相关文章推荐
- “1000万字符串,去掉重复”的一些思考和java实现
- Java实现去掉字符串重复字母的方法示例
- 输入一串不重复的字符串,列出所有可能的组合(java实现)
- Java_去掉字符串中重复的子字符串
- Java 去掉字符串中重复的子字符串
- java实现如何在字符串中找到第一个不重复的字符
- java去掉字符串数组中重复的值
- 给定一字符串,求字符串中第一次出现的非重复的字符,例如abaaedgff,输出b.(Java 实现)
- 如何用java实现不同编码方式字符串的转换(包含异常处理、重复不终止输入、缓冲区bufferedreader的使用)
- Java实现读取文章中重复出现的中文字符串
- 用java实现输出字符串中第一个出现不重复的字符
- (Java)怎么去掉字符串数组中重复的值?
- 用Java正则去掉字符串中重复出现的字符
- Map实现字符串内重复字符数的计算(Java之二)
- Java数组,去掉重复值、增加、删除数组元素的实现方法
- java实现查找数组中重复次数最多的字符串
- java 判断三个字符串中是否有相等,去掉重复字符串输出
- Java学习笔记---实现两个ArrayList合并,并去掉重复数据的算法
- Java 去掉字符串中的重复数据
- java 去掉字符串中的重复数据