【戀上數據結構】前綴樹 Tire 學習筆記

Tire

需求分析

如何判斷一堆不重復的字符串是否以某個前綴開頭?

  • Set\Map 存儲字符串(不重復)
  • 遍歷所有字符串進行判斷
  • 缺點:時間復雜度 O(n)

有沒有更優的數據結構實現前綴搜索?

Tire(和 Tree 同音)

簡介

  • Trie 也叫做字典樹、前綴樹 (Prefix Tree)、單詞查找樹。
  • Trie 搜索字符串的效率主要跟字符串的長度有關。

假設使用 Trie 存儲 catdogdoggydoescastadd 六個單詞,結果如下所示

在這里插入圖片描述

接口設計

有兩種設計方案:

  1. 第一種僅僅是存儲字符串。(像 set 集合)
  2. 第二種是存儲字符串的同時可以再存儲一個 value(像 map 接口)

分析:

第二種設計方案更為通用,比如說我們要做一個通訊錄,以某個人的姓名作為 key,然后以他的詳細信息作為 value(其他電話號碼、郵箱、生日等各種詳細信息)

public interface Trie <V> {int size(); boolean isEmpty(); void clear(); boolean contains(String str); V add(String str, V value); V remove(String str); boolean starswith(String prefix);
}

在這里插入圖片描述

Node 設計

孩子節點集合解析(HashMap<Character, Node<V>> children;):

  • key 相當于代表的是路徑值,Character 字符類型可以是英文也可以是中文
  • value 是嵌套了當前節點下的所有子節點,方便后面節點值尋找
  • word:true 為已存儲單詞(紅色),false 為非單詞(藍色)
    /*** Trie 中的節點類,包含父節點、孩子節點集合、字符、值以及表示是否為一個完整單詞的標志。** @param <V> 值的類型*/private static class Node<V> {Node<V> parent; // 父節點HashMap<Character, Node<V>> children; // 孩子節點集合Character character; // 字符,為刪除做準備V value; // 節點對應的值,也就是整個單詞boolean word; // 是否為單詞的結尾(是否為一個完整的單詞)/*** 構造函數,初始化節點時需要指定父節點。(在添加節點時用到)** @param parent 父節點*/public Node(Node<V> parent) {this.parent = parent;}}

完整代碼實現附注釋

/*** Trie(字典樹)數據結構,用于存儲字符串集合,支持添加、查詢、刪除等操作。** @param <V> 值的類型*/
public class Trie<V> {/** Trie 中存儲的單詞數量 */private int size;/** 根節點 */private Node<V> root;/*** Trie 中的節點類,包含父節點、孩子節點集合、字符、值以及表示是否為一個完整單詞的標志。** @param <V> 值的類型*/private static class Node<V> {Node<V> parent; // 父節點HashMap<Character, Node<V>> children; // 孩子節點集合Character character; // 字符,為刪除做準備V value; // 節點對應的值,也就是整個單詞boolean word; // 是否為單詞的結尾(是否為一個完整的單詞)/*** 構造函數,初始化節點時需要指定父節點。(在添加節點時用到)** @param parent 父節點*/public Node(Node<V> parent) {this.parent = parent;}}/*** 獲取 Trie 中存儲的單詞數量。** @return Trie 中存儲的單詞數量*/public int size() {return size;}/*** 判斷 Trie 是否為空。** @return 如果 Trie 為空,則返回 true;否則返回 false*/public boolean isEmpty() {return size == 0;}/*** 清空 Trie,將單詞數量重置為 0。*/public void clear() {size = 0;root = null;}/*** 根據指定的鍵獲取對應的值。** @param key 鍵* @return 如果鍵存在且是一個完整的單詞,則返回對應的值;否則返回 null*/public V get(String key) {Node<V> node = node(key);return (node != null && node.word) ? node.value : null;}/*** 判斷 Trie 是否包含指定的鍵。** @param key 鍵* @return 如果 Trie 包含指定的鍵且是一個完整的單詞,則返回 true;否則返回 false*/public boolean contains(String key) {Node<V> node = node(key);return node != null && node.word;}/*** 添加鍵值對到 Trie 中。如果鍵已經存在,則更新對應的值;否則新增一個單詞。** @param key   鍵* @param value 值* @return 如果添加的鍵已經存在,則返回對應的舊值;否則返回 null*/public V add(String key, V value) {keyCheck(key);// 創建根節點if (root == null) {root = new Node<>(null);}// 獲取 Trie 根節點Node<V> node = root;// 獲取鍵的長度int len = key.length();// 遍歷鍵的每個字符for (int i = 0; i < len; i++) {// 獲取當前字符char c = key.charAt(i);// 判斷當前節點的孩子節點集合是否為空boolean emptyChildren = (node.children == null);// 獲取當前字符對應的孩子節點Node<V> childNode = emptyChildren ? null : node.children.get(c);// 如果當前字符對應的孩子節點為空,說明該字符在當前節點的孩子節點集合中不存在if (childNode == null) {// 創建新的孩子節點,并將其加入到當前節點的孩子節點集合中childNode = new Node<>(node);childNode.character = c;// 判斷孩子節點集合是否為空的同時,避免了每次都要創建新的 HashMap 對象,提高了效率node.children = emptyChildren ? new HashMap<>(16) : node.children;node.children.put(c, childNode);}// 將當前節點移動到其對應的孩子節點上,繼續下一層的遍歷node = childNode;}// 1 - 已經存在這個單詞, 覆蓋, 返回舊值if (node.word) {V oldValue = node.value;node.value = value;return oldValue;}// 2 - 不存在這個單詞, 新增這個單詞node.word = true;node.value = value;size++;return null;}/*** 移除 Trie 中的指定鍵。如果鍵存在且是一個完整的單詞,將其從 Trie 中移除。** @param key 鍵* @return 如果鍵存在且是一個完整的單詞,則返回對應的值;否則返回 null*/public V remove(String key) {Node<V> node = node(key);// 如果不是單詞結尾,不用作任何處理if (node == null || !node.word) {return null;}size--;V oldValue = node.value;// 如果還有子節點if (node.children != null && !node.children.isEmpty()) {node.word = false;node.value = null;return oldValue;}// 沒有子節點Node<V> parent = null;while ((parent = node.parent) != null) {parent.children.remove(node.character);if (parent.word || !parent.children.isEmpty()) {break;}node = parent;}return oldValue;}/*** 判斷 Trie 是否包含指定前綴。** @param prefix 前綴* @return 如果 Trie 包含指定前綴,則返回 true;否則返回 false*/public boolean startsWith(String prefix) {return node(prefix) != null;}/*** 根據傳入字符串,找到最后一個節點。* 例如輸入 dog* 找到 g** @param key 鍵* @return 如果鍵存在,則返回對應的節點;否則返回 null*/private Node<V> node(String key) {keyCheck(key);Node<V> node = root;int len = key.length();for (int i = 0; i < len; i++) {if (node == null || node.children == null || node.children.isEmpty()) {return null;}char c = key.charAt(i);node = node.children.get(c);}return node;}/*** 檢查鍵是否合法,不允許為空。** @param key 鍵*/private void keyCheck(String key) {if (key == null || key.length() == 0) {throw new IllegalArgumentException("key must not be empty");}}
}

總結

  1. Trie 的優點:搜索前綴的效率主要跟前綴的長度有關

  2. Trie 的缺點:需要耗費大量的內存(一個字符一個節點),因此還有待改進

  3. 更多 Trie 相關的數據結構和算法

    • Double-array Trie、Suffix Tree(后綴樹)、Patricia Tree、Crit-bit Tree、AC 自動機

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

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

相關文章

Rust測試字符串的移動,Move

代碼創建了一個結構體&#xff0c;結構體有test1 字符串&#xff0c;還有指向字符串的指針。一共創建了兩個。 然后我們使用swap 函數 交換兩個結構體內存的內容。 最后如上圖。相同的地址&#xff0c;變成了另外結構體的內容。注意看指針部分&#xff0c;還是指向原來的地址…

input、el-input輸入框輸入規則

一、input 只能輸入框只能輸入正整數&#xff0c;輸入同時禁止了以0開始的數字輸入&#xff0c;防止被轉化為其他進制的數值。 <!-- 不能輸入零時--> <input typetext οninput"valuevalue.replace(/^(0)|[^\d]/g,)"><!-- 能輸入零時--> <inp…

luceda ipkiss教程 43:畫漸變圓弧型波導

案例分享&#xff1a; from si_fab import all as pdk import ipkiss3.all as i3 from ipcore.properties.restrictions import RestrictTuple from ipkiss.geometry.shapes.modifiers import __ShapePathBase__ import numpy as np from math import atan2class ShapePathTa…

[java]學生管理系統

一、學生類 首先創建一個學生類&#xff0c;定義學號姓名年齡居住地 public class Student {private String id;private String name;private String age;private String address;//構造函數public Student(String id, String name, String age, String address) {this.id i…

54.grpc實現文件上傳和下載

文章目錄 一&#xff1a;簡介1. 什么是grpc2. 為什么我們要用grpc 二&#xff1a;grpc的hello world1、 定義hello.proto文件2、生成xxx_grpc.pb.go文件3、生成xxx.pb.go結構體文件4、編寫服務代碼service.go5、編寫客戶端代碼client.go 三、服務端流式傳輸&#xff1a;文件下載…

AIOps、微服務和云平臺

數字景觀正在從整體轉向微服務、基于云的服務。企業和公司需要適應不斷變化的技術格局并跟上變化。系統變得越來越復雜并且不容易管理。我將嘗試解釋一些較新的架構方法、趨勢&#xff0c;并提供對 AIOps 的見解以及它如何幫助解決這個問題。 微服務 微服務架構正在成為最受歡…

什么是web組態?一文讀懂web組態

隨著工業4.0的到來&#xff0c;物聯網、大數據、人工智能等技術的融合應用&#xff0c;使得工業領域正在經歷一場深刻的變革。在這個過程中&#xff0c;web組態技術以其獨特的優勢&#xff0c;正在逐漸受到越來越多企業的關注和認可。那么&#xff0c;什么是web組態&#xff1f…

android-android源碼目錄

android源碼目錄 Android.bp art bionic bootable bootstrap.bash build build.sh compatibility cts dalvik developers development device external frameworks hardware IMAGE javaenv.sh kernel libcore libnativehelper Makefile mkcombinedroot mkimage_ab.sh mkimage.…

我的創作紀念日——一年

機緣 初心始于對技術的熱愛和分享知識的渴望。最初&#xff0c;我在一次練習中遇到了一些問題&#xff0c;通過解決這些問題并將解決方案記錄下來&#xff0c;我意識到分享經驗對自己和他人都非常有價值。于是&#xff0c;我開始在博客和社交平臺上記錄日常學習過程、撰寫技術…

uni-app 獲取PAD激光測溫方式 (uni-app安卓獲取廣播內容)

直接在onload執行下列代碼 var main plus.android.runtimeMainActivity(); //獲取activityvar context plus.android.importClass(android.content.Context); //上下文var receiver plus.android.implements(io.dcloud.feature.internal.reflect.BroadcastReceiver, {onRece…

動力未來:特斯拉 Model S 電池技術一覽

電動汽車是當今最具創新性和前景的交通工具之一,它們不僅能夠提供高效、環保的駕駛體驗,還能夠減少對化石燃料的依賴,促進可持續發展。在電動汽車領域,特斯拉 Model S 是一款引領潮流的產品,它以其豪華、強勁的性能和尖端的電池技術而聞名。本文將為您介紹特斯拉 Model S …

【springboot設計源碼】慶陽非物質文化遺產展示平臺課題背景、目的、意義、研究方法

該項目含有源碼、文檔、PPT、配套開發軟件、軟件安裝教程、項目發布教程等學習內容。 目錄 一、項目介紹&#xff1a; 二、文檔學習資料&#xff1a; 三、模塊截圖&#xff1a; 四、開發技術與運行環境&#xff1a; 五、代碼展示&#xff1a; 六、數據庫表截圖&#xff1…

即時通訊技術文集(第26期):實時音視頻技術合集(Part1) [共16篇]

為了更好地分類閱讀 52im.net 總計1000多篇精編文章&#xff0c;我將在每周三推送新的一期技術文集&#xff0c;本次是第26 期。 [- 1 -] 實時語音聊天中的音頻處理與編碼壓縮技術簡述 [鏈接] http://www.52im.net/thread-825-1-1.html [摘要] 在視頻或者音頻通話過程中&…

2023-12-09 LeetCode每日一題(下一個更大的數值平衡數)

2023-12-09每日一題 一、題目編號 2048. 下一個更大的數值平衡數二、題目鏈接 點擊跳轉到題目位置 三、題目描述 如果整數 x 滿足&#xff1a;對于每個數位 d &#xff0c;這個數位 恰好 在 x 中出現 d 次。那么整數 x 就是一個 數值平衡數 。 給你一個整數 n &#xff0…

數據結構和算法專題---4、限流算法與應用

本章我們會對限流算法做個簡單介紹&#xff0c;包括常用的限流算法&#xff08;計數器、漏桶算法、令牌桶案發、滑動窗口&#xff09;的概述、實現方式、典型場景做個說明。 什么是限流算法 限流是對系統的一種保護措施。即限制流量請求的頻率&#xff08;每秒處理多少個請求…

11_企業架構web服務器文件及時同步

企業架構web服務器的文件及時同步 學習目標和內容 1、能夠理解為何要服務器間文件同步 2、能夠簡單描述實現文件同步的幾種方式 3、能夠實現服務器文件實時同步的案例 一、同步文件介紹 1、服務器文件同步的必要性 根據業務發展需求&#xff0c;業務網站架構已經發展到以上模式…

Linux文件結構與文件權限

基于centos了解Linux文件結構 了解一下文件類型 Linux采用的一切皆文件的思想&#xff0c;將硬件設備、軟件等所有數據信息都以文件的形式呈現在用戶面前&#xff0c;這就使得我們對計算機的管理更加方便。所以本篇文章會對Linux操作系統的文件結構和文件權限進行講解。 首先…

單元測試Nunit的幾種斷言

Nunit提供了一些輔助函數用于確定好某個被測試函數是否正常工作。通常把這些函數稱為斷言 斷言是單元測試最基本的組成部分。因此&#xff0c;NUnit程序庫以Assert類的靜態方法的形式提供了不同形式的多種斷言 1. Assert.AreEqual&#xff1a;比較兩個值是否相等。用于比較數…

Qt生成動態鏈接庫并使用動態鏈接庫

項目結構 整個工程由一個主程序構成和一個模塊構成(dll)。整個工程的結構目錄如下 Define.priMyProject.proMyProject.pro.user ---bin ---MainProgrammain.cppMainProgram.proMainProgram.pro.userwidget.cppwidget.hwidget.ui ---MathDllMathDll.proMathDll.pro.userMyMath.…

Axios 攔截器實戰教程:簡單易懂

Axios 提供了一種稱為 “攔截器&#xff08;interceptors&#xff09;” 的功能&#xff0c;使我們能夠在請求或響應被發送或處理之前對它們進行全局處理。攔截器為我們提供了一種簡潔而強大的方式來轉換請求和響應、進行錯誤處理、添加認證信息等操作。在本文中&#xff0c;我…