為了準備阿里的網上筆試,這幾天回顧了資料結構.看到字典樹時,突然發現四六級的高頻詞可以用字典樹找出來的.(應該不會是一個一個數出來的吧....)
構造字典樹的過程如下:
1.首先確定樹節點需要用怎麼樣的資料結構,我是這樣寫的:
public class TrieTreeNode {/** * 節點深度 */public short depth;/** * 存放當前節點的所有子節點 */public Map<Integer, TrieTreeNode> children = new HashMap<Integer, TrieTreeNode>();/** * 是否為單詞的結尾 */public boolean isTail = false;/** * 雙親節點 */public TrieTreeNode parent;/** * 可以是a-z中的任意字母 */public char value;/** * 當單詞相同時,wordCount++,用於計算相同的單詞個數 */public int wordCount = 0;/** * 存放一整個單詞 */public StringBuilder word = new StringBuilder();public TrieTreeNode() {// TODO Auto-generated constructor stub}}2.通過節點的parent,children屬性,來串連各個節點.每一個單詞開始,都先將root節點作為雙親節點,單詞中的每個字元來構造子節點,如果子節點存在,則直接使用,否則建立.
/** * 通過給定的字串建立字典樹,暫時只支援英文 * @param data 用於建立字典樹的字串,暫不支援中文. */public void createTrieTree(String data) {//分解出文本中的單詞String[] strArr = data.split(regex);//建立字典樹的根節點//將split後的str數組讀入字典樹for (String s : strArr) {//單個的字母不考慮if (s.length() > 1) {//每一個單詞開始,都以root作為第一個雙親節點TrieTreeNode parent = root;//開始迴圈每一個單詞for (short i = 0; i < s.length(); i++) {char c = s.charAt(i);//這裡不考慮大寫,只管小寫.c = (char) (c < 'a' ? c + 32 : c);//建立子節點TrieTreeNode child = createChildTrieTree(c, i, parent);int key = child.value * 100 + child.depth;parent.children.put(key, child);parent = child;}//每一個單詞迴圈完畢後,都將最後一個節點作為尾部.parent.isTail = true;//每次遇到相同的單詞都將wordCount++int wordCount = ++parent.wordCount;String word = parent.word.toString();//將單詞和單詞出現次數放入mapwordCountMap.put(word, new WordAndCount(word, wordCount));}}}/** * 用於建立子節點 * @param c * @param depth * @param parent * @return */private TrieTreeNode createChildTrieTree(char c, short depth,TrieTreeNode parent) {//通過雙親節點,數的深度和字元c,3個條件判斷是否已經存在該子節點,如果存在,直接擷取.TrieTreeNode child = getChild(c, depth, parent);//如果不存在,就建立一個,並將參數賦給它.if (child == null) {child = new TrieTreeNode();child.depth = depth;child.parent = parent;child.value = c;//用於儲存單詞,通過isTail的配合,可以省去搜尋單詞的過程.child.word.append(parent.word).append(c);}return child;}/** * 根據字元c,樹深度和雙親節點,擷取子節點. * @param c * @param depth * @param parent * @return */private TrieTreeNode getChild(char c, short depth, TrieTreeNode parent) {// TODO Auto-generated method stub//每一個雙親節點,都有一個HashMap來存放所有的子節點,key是根據c和depth來合成的.Map<Integer, TrieTreeNode> children = parent.children;//因為字典樹的深度不超過26,所以十位和個位讓depth來存放,百位以上讓字元c來存放,這就可以構成一個獨一無二的key來對應一個valueint key = c * 100 + depth;return children.get(key);}
3.通過如上步驟,字典樹已經構造完成了.接下來是讀值了,通過maker.getWordCountMap(),即可獲得所有單詞和它的出現次數.如果想排序顯示,可以調用TrieTreeMaker中的sort方法進行排序.如果還有更特別的需求,可以通過get方法擷取root節點,對它進行遞迴遍曆.
最後,附上測試代碼和資料:
@Testpublic void test() {String filePath = "C:\\logs\\file.log";File srcFile = new File(filePath);System.out.println("File Size:" + srcFile.length() / 1024 / 1024.0+ "M");long end2, end, start = System.nanoTime();TrieTreeMaker maker = new TrieTreeMaker();maker.setCapacity(1024 * 1024 * 20);//通過給定的檔案來構造字典樹,也可通過調用createTrieTree("xxxx")實現.maker.obtainDataAndCreateTrieTree(srcFile);end = System.nanoTime();System.out.println("After Creation is accomplished:"+ BigDecimal.valueOf(end - start, 9));//true代表升序,false代表降序List<Entry<String, WordAndCount>> list = maker.sort(maker.getWordCountMap(), true);end2 = System.nanoTime();System.out.println("After Sort is accomplished:"+ BigDecimal.valueOf(end2 - end, 9));//結果輸出for (Entry<String, WordAndCount> entry : list) {System.out.println(entry.getValue().word + ":"+ entry.getValue().count);}}File Size:15.6962890625M
After Creation is accomplished:2.812941204s
After Sort is accomplished:0.004912021s
org:113792
java:74116
at:71064
springframework:68740
security:62020
web:53172
apache:37100
struts:33516
....由於結果太多,省略.
點擊這裡下載源碼