国产99久久精品_欧美日本韩国一区二区_激情小说综合网_欧美一级二级视频_午夜av电影_日本久久精品视频

最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
當前位置: 首頁 - 科技 - 知識百科 - 正文

javascript trie前綴樹的示例

來源:懂視網 責編:小采 時間:2020-11-27 22:20:25
文檔

javascript trie前綴樹的示例

javascript trie前綴樹的示例:引子 Trie樹(來自單詞retrieval),又稱前綴字,單詞查找樹,字典樹,是一種樹形結構,是一種哈希樹的變種,是一種用于快速檢索的多叉樹結構。 它的優點是:最大限度地減少無謂的字符串比較,查詢效率比哈希表高。 Trie的核心思想是空間換時間。利用字符串
推薦度:
導讀javascript trie前綴樹的示例:引子 Trie樹(來自單詞retrieval),又稱前綴字,單詞查找樹,字典樹,是一種樹形結構,是一種哈希樹的變種,是一種用于快速檢索的多叉樹結構。 它的優點是:最大限度地減少無謂的字符串比較,查詢效率比哈希表高。 Trie的核心思想是空間換時間。利用字符串

引子

Trie樹(來自單詞retrieval),又稱前綴字,單詞查找樹,字典樹,是一種樹形結構,是一種哈希樹的變種,是一種用于快速檢索的多叉樹結構。

它的優點是:最大限度地減少無謂的字符串比較,查詢效率比哈希表高。

Trie的核心思想是空間換時間。利用字符串的公共前綴來降低查詢時間的開銷以達到提高效率的目的。

Trie樹也有它的缺點, 假定我們只對字母與數字進行處理,那么每個節點至少有52+10個子節點。為了節省內存,我們可以用鏈表或數組。在JS中我們直接用數組,因為JS的數組是動態的,自帶優化。

基本性質

  1. 根節點不包含字符,除根節點外的每一個子節點都包含一個字符
  2. 從根節點到某一節點。路徑上經過的字符連接起來,就是該節點對應的字符串
  3. 每個節點的所有子節點包含的字符都不相同

程序實現

// by 司徒正美
class Trie {
 constructor() {
 this.root = new TrieNode();
 }
 isValid(str) {
 return /^[a-z1-9]+$/i.test(str);
 }
 insert(word) {
 // addWord
 if (this.isValid(word)) {
 var cur = this.root;
 for (var i = 0; i < word.length; i++) {
 var c = word.charCodeAt(i);
 c -= 48; //減少”0“的charCode
 var node = cur.son[c];
 if (node == null) {
 var node = (cur.son[c] = new TrieNode());
 node.value = word.charAt(i);
 node.numPass = 1; //有N個字符串經過它
 } else {
 node.numPass++;
 }
 cur = node;
 }
 cur.isEnd = true; //檣記有字符串到此節點已經結束
 cur.numEnd++; //這個字符串重復次數

 return true;
 } else {
 return false;
 }
 }
 remove(word){
 if (this.isValid(word)) {
 var cur = this.root;
 var array = [], n = word.length
 for (var i = 0; i < n; i++) {
 var c = word.charCodeAt(i);
 c = this.getIndex(c)
 var node = cur.son[c];
 if(node){
 array.push(node)
 cur = node
 }else{
 return false
 }
 
 }
 if(array.length === n){
 array.forEach(function(){
 el.numPass--
 })
 cur.numEnd --
 if( cur.numEnd == 0){
 cur.isEnd = false
 } 
 }
 }else{
 return false
 }
 }
 preTraversal(cb){//先序遍歷
 function preTraversalImpl(root, str, cb){ 
 cb(root, str);
 for(let i = 0,n = root.son.length; i < n; i ++){
 let node = root.son[i];
 if(node){
 preTraversalImpl(node, str + node.value, cb);
 }
 }
 } 
 preTraversalImpl(this.root, "", cb);
 }
 // 在字典樹中查找是否存在某字符串為前綴開頭的字符串(包括前綴字符串本身)
 isContainPrefix(word) {
 if (this.isValid(word)) {
 var cur = this.root;
 for (var i = 0; i < word.length; i++) {
 var c = word.charCodeAt(i);
 c -= 48; //減少”0“的charCode
 if (cur.son[c]) {
 cur = cur.son[c];
 } else {
 return false;
 }
 }
 return true;
 } else {
 return false;
 }
 }
 isContainWord(str) {
 // 在字典樹中查找是否存在某字符串(不為前綴)
 if (this.isValid(word)) {
 var cur = this.root;
 for (var i = 0; i < word.length; i++) {
 var c = word.charCodeAt(i);
 c -= 48; //減少”0“的charCode
 if (cur.son[c]) {
 cur = cur.son[c];
 } else {
 return false;
 }
 }
 return cur.isEnd;
 } else {
 return false;
 }
 }
 countPrefix(word) {
 // 統計以指定字符串為前綴的字符串數量
 if (this.isValid(word)) {
 var cur = this.root;
 for (var i = 0; i < word.length; i++) {
 var c = word.charCodeAt(i);
 c -= 48; //減少”0“的charCode
 if (cur.son[c]) {
 cur = cur.son[c];
 } else {
 return 0;
 }
 }
 return cur.numPass;
 } else {
 return 0;
 }
 }
 countWord(word) {
 // 統計某字符串出現的次數方法
 if (this.isValid(word)) {
 var cur = this.root;
 for (var i = 0; i < word.length; i++) {
 var c = word.charCodeAt(i);
 c -= 48; //減少”0“的charCode
 if (cur.son[c]) {
 cur = cur.son[c];
 } else {
 return 0;
 }
 }
 return cur.numEnd;
 } else {
 return 0;
 }
 }
}

class TrieNode {
 constructor() {
 this.numPass = 0;//有多少個單詞經過這節點
 this.numEnd = 0; //有多少個單詞就此結束
 this.son = [];
 this.value = ""; //value為單個字符
 this.isEnd = false;
 }
}

我們重點看一下TrieNode與Trie的insert方法。 由于字典樹是主要用在詞頻統計,因此它的節點屬性比較多, 包含了numPass, numEnd但非常重要的屬性。

insert方法是用于插入重詞,在開始之前,我們必須判定單詞是否合法,不能出現 特殊字符與空白。在插入時是打散了一個個字符放入每個節點中。每經過一個節點都要修改numPass。

優化

現在我們每個方法中,都有一個c=-48的操作,其實數字與大寫字母與小寫字母間其實還有其他字符的,這樣會造成無謂的空間的浪費

// by 司徒正美
getIndex(c){
 if(c < 58){//48-57
 return c - 48
 }else if(c < 91){//65-90
 return c - 65 + 11
 }else {//> 97 
 return c - 97 + 26+ 11
 }
 }

然后相關方法將c-= 48改成c = this.getIndex(c)即可

測試

var trie = new Trie(); 
 trie.insert("I"); 
 trie.insert("Love"); 
 trie.insert("China"); 
 trie.insert("China"); 
 trie.insert("China"); 
 trie.insert("China"); 
 trie.insert("China"); 
 trie.insert("xiaoliang"); 
 trie.insert("xiaoliang"); 
 trie.insert("man"); 
 trie.insert("handsome"); 
 trie.insert("love"); 
 trie.insert("Chinaha"); 
 trie.insert("her"); 
 trie.insert("know"); 
 var map = {}
 trie.preTraversal(function(node, str){
 if(node.isEnd){
 map[str] = node.numEnd
 }
 })
 for(var i in map){
 console.log(i+" 出現了"+ map[i]+" 次")
 }
 console.log("包含Chin(包括本身)前綴的單詞及出現次數:"); 
 //console.log("China")
 var map = {}
 trie.preTraversal(function(node, str){
 if(str.indexOf("Chin") === 0 && node.isEnd){
 map[str] = node.numEnd
 }
 })
 for(var i in map){
 console.log(i+" 出現了"+ map[i]+" 次")
 }

Trie樹和其它數據結構的比較

Trie樹與二叉搜索樹

二叉搜索樹應該是我們最早接觸的樹結構了,我們知道,數據規模為n時,二叉搜索樹插入、查找、刪除操作的時間復雜度通常只有O(log n),最壞情況下整棵樹所有的節點都只有一個子節點,退變成一個線性表,此時插入、查找、刪除操作的時間復雜度是O(n)。

通常情況下,Trie樹的高度n要遠大于搜索字符串的長度m,故查找操作的時間復雜度通常為O(m),最壞情況下的時間復雜度才為O(n)。很容易看出,Trie樹最壞情況下的查找也快過二叉搜索樹。

文中Trie樹都是拿字符串舉例的,其實它本身對key的適宜性是有嚴格要求的,如果key是浮點數的話,就可能導致整個Trie樹巨長無比,節點可讀性也非常差,這種情況下是不適宜用Trie樹來保存數據的;而二叉搜索樹就不存在這個問題。

Trie樹與Hash表

考慮一下Hash沖突的問題。Hash表通常我們說它的復雜度是O(1),其實嚴格說起來這是接近完美的Hash表的復雜度,另外還需要考慮到hash函數本身需要遍歷搜索字符串,復雜度是O(m)。在不同鍵被映射到“同一個位置”(考慮closed hashing,這“同一個位置”可以由一個普通鏈表來取代)的時候,需要進行查找的復雜度取決于這“同一個位置”下節點的數目,因此,在最壞情況下,Hash表也是可以成為一張單向鏈表的。

Trie樹可以比較方便地按照key的字母序來排序(整棵樹先序遍歷一次就好了),這跟絕大多數Hash表是不同的(Hash表一般對于不同的key來說是無序的)。

在較理想的情況下,Hash表可以以O(1)的速度迅速命中目標,如果這張表非常大,需要放到磁盤上的話,Hash表的查找訪問在理想情況下只需要一次即可;但是Trie樹訪問磁盤的數目需要等于節點深度。

很多時候Trie樹比Hash表需要更多的空間,我們考慮這種一個節點存放一個字符的情況的話,在保存一個字符串的時候,沒有辦法把它保存成一個單獨的塊。Trie樹的節點壓縮可以明顯緩解這個問題,后面會講到。

Trie樹的改進

按位Trie樹(Bitwise Trie)

原理上和普通Trie樹差不多,只不過普通Trie樹存儲的最小單位是字符,但是Bitwise Trie存放的是位而已。位數據的存取由CPU指令一次直接實現,對于二進制數據,它理論上要比普通Trie樹快。

節點壓縮。

分支壓縮:對于穩定的Trie樹,基本上都是查找和讀取操作,完全可以把一些分支進行壓縮。例如,前圖中最右側分支的inn可以直接壓縮成一個節點“inn”,而不需要作為一棵常規的子樹存在。Radix樹就是根據這個原理來解決Trie樹過深問題的。

節點映射表:這種方式也是在Trie樹的節點可能已經幾乎完全確定的情況下采用的,針對Trie樹中節點的每一個狀態,如果狀態總數重復很多的話,通過一個元素為數字的多維數組(比如Triple Array Trie)來表示,這樣存儲Trie樹本身的空間開銷會小一些,雖說引入了一張額外的映射表。

前綴樹的應用

前綴樹還是很好理解,它的應用也是非常廣的。

(1)字符串的快速檢索

字典樹的查詢時間復雜度是O(logL),L是字符串的長度。所以效率還是比較高的。字典樹的效率比hash表高。

(2)字符串排序

從上圖我們很容易看出單詞是排序的,先遍歷字母序在前面。減少了沒必要的公共子串。

(3)最長公共前綴

inn和int的最長公共前綴是in,遍歷字典樹到字母n時,此時這些單詞的公共前綴是in。

(4)自動匹配前綴顯示后綴

我們使用辭典或者是搜索引擎的時候,輸入appl,后面會自動顯示一堆前綴是appl的東東吧。那么有可能是通過字典樹實現的,前面也說了字典樹可以找到公共前綴,我們只需要把剩余的后綴遍歷顯示出來即可。

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

文檔

javascript trie前綴樹的示例

javascript trie前綴樹的示例:引子 Trie樹(來自單詞retrieval),又稱前綴字,單詞查找樹,字典樹,是一種樹形結構,是一種哈希樹的變種,是一種用于快速檢索的多叉樹結構。 它的優點是:最大限度地減少無謂的字符串比較,查詢效率比哈希表高。 Trie的核心思想是空間換時間。利用字符串
推薦度:
  • 熱門焦點

最新推薦

猜你喜歡

熱門推薦

專題
Top
主站蜘蛛池模板: 亚洲欧美自拍偷拍 | a在线视频| 日本国产最新一区二区三区 | 国产日产精品_国产精品毛片 | 日韩欧美国产高清 | 国产视频首页 | 国产精品国产欧美综合一区 | 欧美区在线观看 | 91在线 一区 二区三区 | 国产日韩欧美一区二区三区视频 | 亚洲一页 | 国产亚洲第一页 | 欧美va在线 | 国产一级片免费看 | 亚洲欧洲高清 | 亚洲天码中文字幕第一页 | 欧美一区二区三区四区视频 | 国产在线精选免费视频8x | 久久国产精品成人免费古装 | 国产欧美一区二区精品性色 | 成人久久久精品乱码一区二区三区 | 日韩毛片大全 | 久久精品99久久香蕉国产色戒 | 成人欧美一区二区三区在线 | 国产成人调教视频在线观看 | 日本高清在线播放一区二区三区 | 成人午夜精品久久久久久久小说 | 国产精选免费视频 | 最新国产精品视频免费看 | 国产a级网站 | 波多野结衣网站 | 国产精品亚洲片在线观看不卡 | 在线欧美日韩 | 欧美亚洲国产日韩一区二区三区 | 99久久国产综合精品成人影院 | 中文字幕日韩有码 | 在线 v亚洲 v欧美v 专区 | 国产一区二区三区成人久久片 | 最新国产精品亚洲二区 | 久久91精品国产91久久户 | 久久精品福利 |