字典樹
多用於單詞查找的樹形結構圖
又稱單詞查找樹,Trie樹,是一種樹形結構,是一種哈希樹的變種。典型應用是用於統計,排序和保存大量的字元串(但不僅限於字元串),所以經常被搜索引擎系統用於文本詞頻統計。它的優點是:利用字元串的公共前綴來減少查詢時間,最大限度地減少無謂的字元串比較,查詢效率比哈希樹高。
它有3個基本性質:
根節點不包含字元,除根節點外每一個節點都只包含一個字元;從根節點到某一節點,路徑上經過的字元連接起來,為該節點對應的字元串;每個節點的所有子節點包含的字元都不相同。
搜索字典項目的方法為:
(1) 從根結點開始一次搜索;
(2) 取得要查找關鍵詞的第一個字母,並根據該字母選擇對應的子樹並轉到該子樹繼續進行檢索;
(3) 在相應的子樹上,取得要查找關鍵詞的第二個字母,並進一步選擇對應的子樹進行檢索。
(4) 迭代過程……
(5) 在某個結點處,關鍵詞的所有字母已被取出,則讀取附在該結點上的信息,即完成查找。其他操作類似處理
給出N個單片語成的熟詞表,以及一篇全用小寫英文書寫的文章,請你按最早出現的順序寫出所有不在熟詞表中的生詞。在這道題中,我們可以用數組枚舉,用哈希,用字典樹,先把熟詞建一棵樹,然後讀入文章進行比較,這種方法效率是比較高的。
給定N個互不相同的僅由一個單詞構成的英文名,讓你將他們按字典序從小到大輸出,用字典樹進行排序,採用數組的方式創建字典樹,這棵樹的每個結點的所有兒子很顯然地按照其字母大小排序。對這棵樹進行先序遍歷即可。
對所有串建立字典樹,對於兩個串的最長公共前綴的長度即他們所在的結點的公共祖先個數,於是,問題就轉化為當時公共祖先問題。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | class Trie: # 初始化 def __init__(self): self.root = {} self.end = "#" # 添加單詞 def add(self, word: str): node = self.root for char in word: node = node.setdefault(char, {}) node[self.end] = None # 搜索單詞是否存在 def search(self, word): node = self.root for char in word: if char not in node: return False node = node[char] return self.end in node |
Java
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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | packagecom.suning.search.test.tree.trie; public class Trie { private int SIZE=26; private TrieNode root;//字典樹的根 Trie() //初始化字典樹 { root=new TrieNode(); } private class TrieNode //字典樹節點 { private int num;//有多少單詞通過這個節點,即由根至該節點組成的字元串模式出現的次數 private TrieNode[] son;//所有的兒子節點 private boolean isEnd;//是不是最後一個節點 private char val;//節點的值 private boolean haveSon; TrieNode() { num=1; son=new TrieNode[SIZE]; isEnd=false; haveSon=false; } } //建立字典樹 public void insert(String str) //在字典樹中插入一個單詞 { if(str==null||str.length()==0) { return; } TrieNode node=root; char[]letters=str.toCharArray(); for(int i=0,len=str.length(); i { int pos=letters[i]-'a'; if(node.son[pos]==null) { node.haveSon = true; node.son[pos]=newTrieNode(); node.son[pos].val=letters[i]; } else { node.son[pos].num++; } node=node.son[pos]; } node.isEnd=true; } //計算單詞前綴的數量 public int countPrefix(Stringprefix) { if(prefix==null||prefix.length()==0) { return-1; } TrieNode node=root; char[]letters=prefix.toCharArray(); for(inti=0,len=prefix.length(); i { int pos=letters[i]-'a'; if(node.son[pos]==null) { return 0; } else { node=node.son[pos]; } } return node.num; } //列印指定前綴的單詞 public String hasPrefix(String prefix) { if (prefix == null || prefix.length() == 0) { return null; } TrieNode node = root; char[] letters = prefix.toCharArray(); for (int i = 0, len = prefix.length(); i < len; i++) { int pos = letters[i] - 'a'; if (node.son[pos] == null) { return null; } else { node = node.son[pos]; } } preTraverse(node, prefix); return null; } // 遍歷經過此節點的單詞. public void preTraverse(TrieNode node, String prefix) { if (node.haveSon) { for (TrieNode child : node.son) { if (child!=null) { preTraverse(child, prefix+child.val); } } return; } System.out.println(prefix); } //在字典樹中查找一個完全匹配的單詞. public boolean has(Stringstr) { if(str==null||str.length()==0) { return false; } TrieNode node=root; char[]letters=str.toCharArray(); for(inti=0,len=str.length(); i { intpos=letters[i]-'a'; if(node.son[pos]!=null) { node=node.son[pos]; } else { return false; } } return node.isEnd; } //前序遍歷字典樹. public void preTraverse(TrieNodenode) { if(node!=null) { System.out.print(node.val+"-"); for(TrieNodechild:node.son) { preTraverse(child); } } } public TrieNode getRoot() { return this.root; } { Trietree=newTrie(); String[]strs= {"banana","band","bee","absolute","acm",}; String[]prefix= {"ba","b","band","abc",}; for(Stringstr:strs) { tree.insert(str); } System.out.println(tree.has("abc")); tree.preTraverse(tree.getRoot()); System.out.println(); //tree.printAllWords(); for(Stringpre:prefix) { int num=tree.countPrefix(pre); System.out.println(pre+""+num); } } } |
C語言
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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | #define MAX 26//字符集大小 typedef struct TrieNode { int nCount;//記錄該字元出現次數 struct TrieNode* next[MAX]; }TrieNode; TrieNode Memory[1000000]; int allocp=0; void InitTrieRoot(TrieNode* *pRoot) { *pRoot=NULL; } TrieNode* CreateTrieNode() { int i; TrieNode *p; p=&Memory[allocp++]; p->nCount=1; for(i=0;i { p->next[i]=NULL; } return p; } void InsertTrie(TrieNode* *pRoot,char *s) { int i,k; TrieNode*p; if(!(p=*pRoot)) { p=*pRoot=CreateTrieNode(); } i=0; while(s[i]) { k=s[i++]-'a';//確定branch if(!p->next[k]) p->next[k]=CreateTrieNode(); else p->next[k]->nCount++; p=p->next[k]; } } //查找 int SearchTrie(TrieNode* *pRoot,char *s) { TrieNode *p; int i,k; if(!(p=*pRoot)) { return0; } i=0; while(s[i]) { k=s[i++]-'a'; if(p->next[k]==NULL) return 0; p=p->next[k]; } return p->nCount; } |
C++
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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | #include #include <conio.h> using namespace std; namespace trie { template struct nod { T reserved; nod nod() { memset(this, 0, sizeof *this); } ~nod() { for (unsigned i = 0; i < CHILD_MAX; i++) delete child[i]; } void Traversal(char *str, unsigned index) { unsigned i; for (i = 0; i < index; i++) cout << str[i]; cout << '\t' << reserved << endl; for (i = 0; i < CHILD_MAX; i++) { if (child[i]) { str[index] = i; child[i]->Traversal(str, index + 1); } } return; } }; template class trie { private: nod public: nod nod bool DeleteStr(char *str); void Traversal() { char str[100]; root.Traversal(str, 0); } }; template nod { nod do { if (now->child[*str] == NULL) now->child[*str] = new nod now = now->child[*str]; } while (*(++str) != '\0'); return now; } template nod { nod do { if (now->child[*str] == NULL) return NULL; now = now->child[*str]; } while (*(++str) != '\0'); return now; } template bool trie { nod int snods = 1; nod nods[0] = &root; do { if (now->child[*str] == NULL) return false; nods[snods++] = now = now->child[*str]; } while (*(++str) != '\0'); snods--; while (snods > 0) { for (unsigned i = 0; i < CHILD_MAX; i++) if (nods[snods]->child[i] != NULL) return true; delete nods[snods]; nods[--snods]->child[*(--str)] = NULL; } return true; } } // namespace trie int main() { //TestProgram trie::trie while (1) { cout << "1 for add a string" << endl; cout << "2 for find a string" << endl; cout << "3 for delete a string" << endl; cout << "4 for traversal" << endl; cout << "5 for exit" << endl; char str[100]; switch (getch()) { case '1': cout << "Input a string: "; cin.getline(str, 100); cout << "This string has existed for " << tree.AddStr(str)->reserved++ << " times." << endl; break; case '2': cout << "Input a string: "; cin.getline(str, 100); trie::nod find = tree.FindStr(str); if (!find) cout << "No found." << endl; else cout << "This string has existed for " << find->reserved << " times." << endl; break; case '3': cout << "Input a string: "; cin.getline(str, 100); cout << "The action is " << (tree.DeleteStr(str) ? "Successful." : "Unsuccessful.") << endl; break; case '4': cout << "Traversal the tree: " << endl; tree.Traversal(); break; case '5': cout << "Exit." << endl; return 0; } } return 0; } |