日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

数据结构与算法专题——第十二题 Trie树

發布時間:2023/12/4 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构与算法专题——第十二题 Trie树 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

今天來聊一聊Trie樹,Trie樹的名字有很多,比如字典樹,前綴樹等等。

一:概念

下面有and,as,at,cn,com這幾個關鍵詞,構建成 trie 樹如下。

從上面圖中,應該可以或多或少的發現一些好玩的特性。

  • 根節點不包含字符,除根節點外的每一個子節點都包含一個字符。

  • 從根節點到某一節點,路徑上經過的字符連接起來,就是該節點對應的字符串。

  • 每個單詞的公共前綴作為一個字符節點保存。

二:使用范圍

既然學Trie樹,肯定要知道這玩意是用來干嘛的?

1. 詞頻統計。

可能有人要說了,詞頻統計簡單啊,一個hash或者一個堆就可以打完收工,但問題來了,如果內存有限呢?還能這么玩嗎?這種限制級條件下就可以用trie樹來壓縮下空間,因為公共前綴都是用一個節點保存的。

2. 前綴匹配

就拿上面的圖來說吧,如果我想獲取所有以 "a" 開頭的字符串,從圖中可以很明顯的看到是:and,as,at,如果不用trie樹,你該怎么做呢?很顯然樸素的做法時間復雜度為O(N2) ,用Trie樹就不一樣了,它可以做到h,h為你檢索單詞的長度,可以說這是秒殺的效果。

舉個例子:現有一個編號為1的字符串”and“,怎樣插入到trie樹中呢?采用動態規劃的思想,將編號”1“計入到每個途徑的節點中,那么以后我們要找”a“,”an“,”and"為前綴的字符串的編號將會輕而易舉。

三:實際操作

到現在為止,我想大家已經對trie樹有了大概的掌握,下面看看如何來實現。

1:定義trie樹節點

為了方便,我也采用純英文字母,大家都知道字母有26個,所以構建的trie樹就是一個26叉樹,每個節點包含26個子節點,實現代碼如下:

///?<summary>///?Trie樹節點///?</summary>public?class?TrieNode{///?<summary>///?26個字符,也就是26叉樹///?</summary>public?TrieNode[]?childNodes;///?<summary>///?詞頻統計///?</summary>public?int?freq;///?<summary>///?記錄該節點的字符///?</summary>public?char?nodeChar;///?<summary>///?插入記錄時的編碼id///?</summary>public?HashSet<int>?hashSet?=?new?HashSet<int>();///?<summary>///?初始化///?</summary>public?TrieNode(){childNodes?=?new?TrieNode[26];freq?=?0;}}

2: 添加操作

既然是26叉樹,那么當前節點的后續子節點是放在當前節點的哪一叉中,也就是放在childNodes中哪一個位置,這里采用 int k = word[0] - 'a' 來計算位置。

///?<summary>///?插入操作///?</summary>///?<param?name="root"></param>///?<param?name="s"></param>public?void?AddTrieNode(ref?TrieNode?root,?string?word,?int?id){if?(word.Length?==?0)return;//求字符地址,方便將該字符放入到26叉樹中的哪一叉中int?k?=?word[0]?-?'a';//如果該叉樹為空,則初始化if?(root.childNodes[k]?==?null){root.childNodes[k]?=?new?TrieNode();//記錄下字符root.childNodes[k].nodeChar?=?word[0];}//該id途徑的節點root.childNodes[k].hashSet.Add(id);var?nextWord?=?word.Substring(1);//說明是最后一個字符,統計該詞出現的次數if?(nextWord.Length?==?0)root.childNodes[k].freq++;AddTrieNode(ref?root.childNodes[k],?nextWord,?id);}

3:刪除操作

刪除操作中,不僅要刪除該節點的字符串編號,還要對詞頻減一操作。

///?<summary>///?刪除操作///?</summary>///?<param?name="root"></param>///?<param?name="newWord"></param>///?<param?name="oldWord"></param>///?<param?name="id"></param>public?void?DeleteTrieNode(ref?TrieNode?root,?string?word,?int?id){if?(word.Length?==?0)return;//求字符地址,方便將該字符放入到26叉樹種的哪一顆樹中int?k?=?word[0]?-?'a';//如果該叉樹為空,則說明沒有找到要刪除的點if?(root.childNodes[k]?==?null)return;var?nextWord?=?word.Substring(1);//如果是最后一個單詞,則減去詞頻if?(word.Length?==?0?&&?root.childNodes[k].freq?>?0)root.childNodes[k].freq--;//刪除途經節點root.childNodes[k].hashSet.Remove(id);DeleteTrieNode(ref?root.childNodes[k],?nextWord,?id);}

4:測試

這里我從網上下載了一套的詞匯表,共2279條詞匯,現在要做的就是檢索 “go” 開頭的詞匯,并統計go出現的頻率。

public?static?void?Main(){Trie?trie?=?new?Trie();var?file?=?File.ReadAllLines(Environment.CurrentDirectory?+?"//1.txt");foreach?(var?item?in?file){var?sp?=?item.Split(new?char[]?{?'?'?},?StringSplitOptions.RemoveEmptyEntries);trie.AddTrieNode(sp.LastOrDefault().ToLower(),?Convert.ToInt32(sp[0]));}Stopwatch?watch?=?Stopwatch.StartNew();//檢索go開頭的字符串var?hashSet?=?trie.SearchTrie("go");foreach?(var?item?in?hashSet){Console.WriteLine("當前字符串的編號ID為:{0}",?item);}watch.Stop();Console.WriteLine("耗費時間:{0}",?watch.ElapsedMilliseconds);Console.WriteLine("\n\ngo?出現的次數為:{0}\n\n",?trie.WordCount("go"));}

下面我們拿著ID到txt中去找一找,嘿嘿,是不是很有意思。

測試文件:http://files.cnblogs.com/huangxincheng/1.zip

總結

以上是生活随笔為你收集整理的数据结构与算法专题——第十二题 Trie树的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。