Trie樹(前綴樹)的實現與應用

Trie樹,也被稱為前綴樹,是一種用于處理字符串的數據結構。它可以高效地進行字符串的插入、刪除和搜索操作,并且能夠快速找到具有相同前綴的字符串。本篇博客將詳細介紹Trie樹的實現原理和應用場景,并給出Java代碼示例。

Trie樹的基本結構

Trie樹的基本結構由節點和邊組成,每個節點表示一個字符,每條邊表示一個字符的連接。從根節點到葉子節點的路徑表示一個完整的字符串。
在Java代碼示例中,我們定義了兩種Trie樹的實現方式。 Trie1 使用數組作為節點的存儲結構, Trie2 使用哈希表作為節點的存儲結構。兩種實現方式在時間復雜度上沒有本質的差異,只是在空間復雜度上有所不同。

Trie樹的基本操作

Trie樹的基本操作包括插入、刪除、搜索和前綴匹配。

插入操作

插入操作用于將一個字符串插入到Trie樹中。具體步驟如下:

  1. 從根節點開始,遍歷字符串的每個字符。
  2. 對于每個字符,判斷是否存在與之對應的子節點。
    • 如果不存在,則創建一個新的節點,并將其作為當前節點的子節點。
    • 如果存在,則將當前節點指向該子節點。
  3. 在遍歷完所有字符后,將當前節點的 end 計數加一,表示該字符串的插入次數。

刪除操作

刪除操作用于從Trie樹中刪除一個字符串。具體步驟如下:

  1. 首先通過搜索操作判斷該字符串是否存在于Trie樹中。
  2. 如果存在,則從根節點開始,遍歷字符串的每個字符。
  3. 對于每個字符,判斷是否存在與之對應的子節點。
    • 如果存在,則將當前節點指向該子節點。
    • 如果不存在,則返回,表示該字符串不存在于Trie樹中。
  4. 在遍歷完所有字符后,將當前節點的 end 計數減一。
  5. 如果當前節點的 pass 計數為0,表示該節點沒有其他字符串經過,可以將其刪除。

搜索操作

搜索操作用于判斷一個字符串在Trie樹中出現的次數。具體步驟如下:

  1. 從根節點開始,遍歷字符串的每個字符。
  2. 對于每個字符,判斷是否存在與之對應的子節點。
    • 如果存在,則將當前節點指向該子節點。
    • 如果不存在,則返回0,表示該字符串不存在于Trie樹中。
  3. 在遍歷完所有字符后,返回當前節點的 end 計數,即表示該字符串在Trie樹中出現的次數。

前綴匹配操作

前綴匹配操作用于統計所有以給定前綴開頭的字符串的出現次數。具體步驟如下:

  1. 從根節點開始,遍歷前綴字符串的每個字符。
  2. 對于每個字符,判斷是否存在與之對應的子節點。
    • 如果存在,則將當前節點指向該子節點。
    • 如果不存在,則返回0,表示沒有以該前綴開頭的字符串。
  3. 在遍歷完所有字符后,返回當前節點的 pass 計數,即表示所有以該前綴開頭的字符串的出現次數。

示例與測試

在代碼示例中,我們通過隨機生成字符串數組的方式進行了多次測試,分別使用 Trie1Trie2Right (正確的實現方式)進行插入、刪除、搜索和前綴匹配操作,并對結果進行了比較驗證。
// 示例代碼

package class08;import java.util.HashMap;// 該程序完全正確
public class Code01_TrieTree {public static class Node1 {public int pass;//記錄字符經過的個數public int end;//記錄字符結束的個數public Node1[] nexts;//節點數組// char tmp = 'b'  (tmp - 'a')public Node1() {pass = 0;end = 0;nexts = new Node1[26];}}public static class Trie1 {private Node1 root;public Trie1() {root = new Node1();}public void insert(String word) {if (word == null) {return;}char[] str = word.toCharArray();Node1 node = root;//頭節點node.pass++;//有字符經過時int path = 0;for (int i = 0; i < str.length; i++) { // 從左往右遍歷字符path = str[i] - 'a'; // 由字符,對應成走向哪條路if (node.nexts[path] == null) {node.nexts[path] = new Node1();}node = node.nexts[path];node.pass++;}node.end++;}public void delete(String word) {if (search(word) != 0) {char[] chs = word.toCharArray();Node1 node = root;node.pass--;int path = 0;for (int i = 0; i < chs.length; i++) {path = chs[i] - 'a';if (--node.nexts[path].pass == 0) {node.nexts[path] = null;return;}node = node.nexts[path];}node.end--;}}// word這個單詞之前加入過幾次public int search(String word) {if (word == null) {return 0;}char[] chs = word.toCharArray();Node1 node = root;int index = 0;for (int i = 0; i < chs.length; i++) {index = chs[i] - 'a';if (node.nexts[index] == null) {return 0;}node = node.nexts[index];}return node.end;}// 所有加入的字符串中,有幾個是以pre這個字符串作為前綴的public int prefixNumber(String pre) {if (pre == null) {return 0;}char[] chs = pre.toCharArray();Node1 node = root;int index = 0;for (int i = 0; i < chs.length; i++) {index = chs[i] - 'a';if (node.nexts[index] == null) {return 0;}node = node.nexts[index];}return node.pass;}}public static class Node2 {public int pass;public int end;public HashMap<Integer, Node2> nexts;public Node2() {pass = 0;end = 0;nexts = new HashMap<>();}}public static class Trie2 {private Node2 root;public Trie2() {root = new Node2();}public void insert(String word) {if (word == null) {return;}char[] chs = word.toCharArray();Node2 node = root;node.pass++;int index = 0;for (int i = 0; i < chs.length; i++) {index = (int) chs[i];if (!node.nexts.containsKey(index)) {node.nexts.put(index, new Node2());}node = node.nexts.get(index);node.pass++;}node.end++;}public void delete(String word) {if (search(word) != 0) {char[] chs = word.toCharArray();Node2 node = root;node.pass--;int index = 0;for (int i = 0; i < chs.length; i++) {index = (int) chs[i];if (--node.nexts.get(index).pass == 0) {node.nexts.remove(index);return;}node = node.nexts.get(index);}node.end--;}}// word這個單詞之前加入過幾次public int search(String word) {if (word == null) {return 0;}char[] chs = word.toCharArray();Node2 node = root;int index = 0;for (int i = 0; i < chs.length; i++) {index = (int) chs[i];if (!node.nexts.containsKey(index)) {return 0;}node = node.nexts.get(index);}return node.end;}// 所有加入的字符串中,有幾個是以pre這個字符串作為前綴的public int prefixNumber(String pre) {if (pre == null) {return 0;}char[] chs = pre.toCharArray();Node2 node = root;int index = 0;for (int i = 0; i < chs.length; i++) {index = (int) chs[i];if (!node.nexts.containsKey(index)) {return 0;}node = node.nexts.get(index);}return node.pass;}}public static class Right {private HashMap<String, Integer> box;public Right() {box = new HashMap<>();}public void insert(String word) {if (!box.containsKey(word)) {box.put(word, 1);} else {box.put(word, box.get(word) + 1);}}public void delete(String word) {if (box.containsKey(word)) {if (box.get(word) == 1) {box.remove(word);} else {box.put(word, box.get(word) - 1);}}}public int search(String word) {if (!box.containsKey(word)) {return 0;} else {return box.get(word);}}public int prefixNumber(String pre) {int count = 0;for (String cur : box.keySet()) {if (cur.startsWith(pre)) {count += box.get(cur);}}return count;}}// for testpublic static String generateRandomString(int strLen) {char[] ans = new char[(int) (Math.random() * strLen) + 1];for (int i = 0; i < ans.length; i++) {int value = (int) (Math.random() * 6);ans[i] = (char) (97 + value);}return String.valueOf(ans);}// for testpublic static String[] generateRandomStringArray(int arrLen, int strLen) {String[] ans = new String[(int) (Math.random() * arrLen) + 1];for (int i = 0; i < ans.length; i++) {ans[i] = generateRandomString(strLen);}return ans;}public static void main(String[] args) {int arrLen = 100;int strLen = 20;int testTimes = 100000;for (int i = 0; i < testTimes; i++) {String[] arr = generateRandomStringArray(arrLen, strLen);Trie1 trie1 = new Trie1();Trie2 trie2 = new Trie2();Right right = new Right();for (int j = 0; j < arr.length; j++) {double decide = Math.random();if (decide < 0.25) {trie1.insert(arr[j]);trie2.insert(arr[j]);right.insert(arr[j]);} else if (decide < 0.5) {trie1.delete(arr[j]);trie2.delete(arr[j]);right.delete(arr[j]);} else if (decide < 0.75) {int ans1 = trie1.search(arr[j]);int ans2 = trie2.search(arr[j]);int ans3 = right.search(arr[j]);if (ans1 != ans2 || ans2 != ans3) {System.out.println("Oops!");}} else {int ans1 = trie1.prefixNumber(arr[j]);int ans2 = trie2.prefixNumber(arr[j]);int ans3 = right.prefixNumber(arr[j]);if (ans1 != ans2 || ans2 != ans3) {System.out.println("Oops!");}}}}System.out.println("finish!");}}

總結

Trie樹是一種高效的字符串處理數據結構,適用于各種需要快速搜索、插入和刪除字符串的場景。本篇博客介紹了Trie樹的原理和實現方式,并給出了Java代碼示例。希望本篇博客能夠幫助讀者更好地理解和應用Trie樹。
以上就是對Trie樹的詳細介紹和應用場景的博客。希望對您有所幫助。如有任何疑問,請隨時提問。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/43703.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/43703.shtml
英文地址,請注明出處:http://en.pswp.cn/news/43703.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MyBatis的入門級環境搭建及增刪改查,詳細易懂

目錄 一.mybatis的簡介 二.MyBatis的環境搭建 2.1 導入pom依賴 2.2 數據庫文件導入連接 2.3 修改web.xml文件 2.4 安裝插件 2.5 配置文件 2.5.1 mybatis.cfg.xml文件 2.5.2 generatorConfig.xml文件 2.6 最后測試生成代碼 三.MyBatis的增刪改查 3.1 寫service類&#xff…

Linux命令200例:nc非常有用的網絡工具(常用)

&#x1f3c6;作者簡介&#xff0c;黑夜開發者&#xff0c;全棧領域新星創作者?。CSDN專家博主&#xff0c;阿里云社區專家博主&#xff0c;2023年6月csdn上海賽道top4。 &#x1f3c6;數年電商行業從業經驗&#xff0c;歷任核心研發工程師&#xff0c;項目技術負責人。 &…

【LVS】3、LVS+Keepalived群集

為什么用它&#xff0c;為了做高可用 服務功能 1.故障自動切換 2.健康檢查 3.節點服務器高可用-HA Keepalived的三個模塊&#xff1a; core&#xff1a;Keepalived的核心&#xff0c;負責主進程的啟動、維護&#xff1b;調用全局配置文件進行加載和解析 vrrp&#xff1a;實…

matlab使用教程(16)—圖論中圖的定義與修改

1.修改現有圖的節點和邊 此示例演示如何使用 addedge 、 rmedge 、 addnode 、 rmnode 、 findedge 、 findnode 及 subgraph 函數訪問和修改 graph 或 digraph 對象中的節點和/或邊。 1.1 添加節點 創建一個包含四個節點和四條邊的圖。s 和 t 中的對應元素用于指定每條…

使用 MBean 和 日志查看 Tomcat 線程池核心屬性數據

文章目錄 CustomTomcatThreadPoolMBeanCustomTomcatThreadPool CustomTomcatThreadPoolMBean com.qww.config;public interface CustomTomcatThreadPoolMBean {String getStatus(); }CustomTomcatThreadPool package com.qww.config;import com.alibaba.fastjson.JSON; impor…

三本書與三場發布會,和鯨社區重新定義編程類書籍從閱讀到實踐新體驗

當 AI 開發者社區配備 AI 基礎設施開發平臺工具時&#xff0c;它還能做什么&#xff1f; 答案是&#xff1a;過去半年&#xff0c;和鯨社區憑借在氣象、醫學、社科等垂直領域的長期積累以及多方伙伴的支持&#xff0c;聯合舉辦了三場新書發布會——從 Python 到 R 語言 、從氣…

Midjourney Prompt 提示詞速查表 v5.2

Midjourney 最新的版本更新正不斷推出令人興奮的新功能。這雖然不斷擴展了我們的AI繪圖工具箱&#xff0c;但有時也會讓我們難以掌握所有實際可以使用的功能和參數。 針對此問題, 小編整理了 "Midjourney Prompt 提示詞速查表"&#xff0c;這是一個非常方便的 Midjo…

Java“牽手“拼多多商品詳情頁面數據獲取方法,拼多多API實現批量商品數據抓取示例

拼多多商城是一個網上購物平臺&#xff0c;售賣各類商品&#xff0c;包括服裝、鞋類、家居用品、美妝產品、電子產品等。要獲取拼多多商品詳情數據&#xff0c;您可以通過開放平臺的接口或者直接訪問拼多多商城的網頁來獲取商品詳情信息。以下是兩種常用方法的介紹&#xff1a;…

Linux:shell腳本數組和腳本免交互

目錄 一、shell數組的定義 二、定義數組的方式 &#xff08;1&#xff09;數組名(value1 value2 value3 value4 ...) &#xff08;2&#xff09;獲取數組的長度 &#xff08;3&#xff09;獲取數組下標對應的值 &#xff08;4&#xff09;數組的遍歷 &#xff08;5&#x…

qsort函數詳解

大家好&#xff0c;我是蘇貝&#xff0c;本篇博客帶大家了解qsort函數&#xff0c;如果你覺得我寫的不錯的話&#xff0c;可以給我一個贊&#x1f44d;嗎&#xff0c;感謝?? 文章目錄 一. qsort函數參數詳解1.數組首元素地址base2.數組的元素個數num和元素所占內存空間大小w…

ThreeJS——在3D地球上標記中國地圖板塊

Threejs3D地球標記中國地圖位置 先看效果 地球預覽視頻效果 用到的庫 TweenJS (動畫庫)用來做相機轉場的動畫Jquery(這里只用到一個 each 循環方法&#xff0c;可以使用 js 去寫)ThreeJS (3D 地球制作)100000.json(全國城市經緯度)d3.v6.js用來設置平面轉3D效果(本來考慮做成…

深入解析IDS/IPS與SSL/TLS和網絡安全

目錄 防火墻 IDS IPS DMZ VPN VPS SSL/TLS 動態IP 靜態IP 防火墻 防火墻是一種網絡安全設備&#xff0c;用于監控和控制網絡流量&#xff0c;保護網絡免受未經授權的訪問、惡意攻擊和威脅。防火墻可以基于規則進行數據包過濾&#xff0c;允許或阻止特定類型的流量通過…

Lead-Lag控制器形式

對于Lead-Lag&#xff08;超前—滯后&#xff09;&#xff0c;有的地方叫做控制器 Controller&#xff0c;有的地方叫補償器 Compensator&#xff0c;有的地方叫濾波器 Filter&#xff0c;都是一個東西。 Lead-Lag也有幾種不同的形式&#xff0c;一種是 G c ( s ) 1 a T s 1…

QT設置widget背景圖片

首先說方法&#xff0c;在給widget或者frame或者其他任何類型的控件添加背景圖時&#xff0c;在樣式表中加入如下代碼&#xff0c;指定某個控件&#xff0c;設置其背景。 類名 # 控件名 { 填充方式&#xff1a;圖片路徑 } 例如&#xff1a; QWidget#Widget {border-image: url…

無涯教程-TensorFlow - 優化器

Optimizers是擴展類&#xff0c;其中包括用于訓練特定模型的附加信息&#xff0c;Optimizers類使用給定的參數初始化&#xff0c;用于提高速度和性能&#xff0c;以訓練特定模型。 TensorFlow的基本Optimizers是- tf.train.Optimizer 此類在tensorflow/python/training/opti…

C語言:深度學習知識儲備

目錄 數據類型 每種類型的大小是多少呢&#xff1f; 變量 變量的命名&#xff1a; 變量的分類&#xff1a; 變量的作用域和生命周期 作用域&#xff1a; 生命周期&#xff1a; 常量 字符串轉義字符注釋 字符串&#xff1a; 轉義字符 操作符&#xff1a; 算術操作符…

圖神經網絡 day2 圖的分類

圖神經網絡基礎算法 1 GCN2 GraphSAGE2.1 采樣&#xff1a;采樣固定長度的鄰居2.2 聚合2.3 GraphSAGE_minibatch2.4 GraphSAGE_embedding 3 GAT4. 圖網絡的分類4.1 遞歸圖神經網絡 RGNN4.2 圖卷積神經網絡GCN4.3 圖注意力網絡 GAT4.4 圖自動編碼 GAE4.5 圖時空網絡 GSTN4.6 圖生…

typeScript 接口和類

工具&#xff1a; PlayGround 接口 接口用來定義對象的結構和類型&#xff0c;描述對象應該具有哪些屬性和方法。 它僅用于聲明&#xff0c;而不是實現&#xff1b; 這對于編寫可重用的代碼非常有用。它可用于&#xff1a; 關鍵字是interface&#xff0c; 注意&#xff1a;它…

OSPF在廣播類型的網絡拓撲中DR和BDR的選舉

指定路由器&#xff08;DR&#xff09;&#xff1a; 一個網段上的其他路由器都和指定路由器&#xff08;DR&#xff09;構成鄰接關系&#xff0c;而不是它們互相之間構成鄰接關系。 備份指定路由器&#xff08;BDR&#xff09;&#xff1a; 當DR出現問題&#xff0c;由BDR接…

redis事務對比Lua腳本區別是什么

redis官方對于lua腳本的解釋&#xff1a;Redis使用同一個Lua解釋器來執行所有命令&#xff0c;同時&#xff0c;Redis保證以一種原子性的方式來執行腳本&#xff1a;當lua腳本在執行的時候&#xff0c;不會有其他腳本和命令同時執行&#xff0c;這種語義類似于 MULTI/EXEC。從別…