資料結構系列——Trie樹
Trie樹,即字典樹,又稱單詞查詢樹或鍵樹,是一種樹形結構,是一種雜湊樹的變種。典型應用是用於統計和排序大量的字串(但不僅限於字串),所以經常被搜尋引擎系統用於文字詞頻統計。它的優點是:最大限度地減少無謂的字串比較,查詢效率比雜湊表高。
Trie樹結構
Trie的核心思想是空間換時間:利用字串的公共字首來降低查詢時間的開銷以達到提高效率的目的。
舉例:將tea,ten,to,in,inn,int幾個單詞構建成一個Trie樹,看一下具體的Tried樹的結構:
圖:Trie樹結構
從圖中可以看出Trie樹的某些特性:
- 根節點不包含字元,除根節點外每一個節點都只包含一個字元。
- 從根節點到某一節點,路徑上經過的字元連線起來,為該節點對應的字串。
- 每個節點的所有子節點包含的字元都不相同。
Trie樹實現
Trie樹的插入、刪除、查詢的操作都是一樣的,只需要簡單的對樹進行一遍遍歷即可,時間複雜度:O(n)(n是字串的長度)。
對於Tried樹的實現可以使用陣列和連結串列兩種方式:
- 陣列:由於我們知道一個Tried樹節點的子節點的數量是固定26個(針對不同情況會不同,比如相容數字,則是36等),所以可以使用固定長度的陣列來儲存節點的子節點
- 優點:在對子節點進行查詢時速度快
- 缺點:浪費空間,不管子節點有多少個,總是需要分配26個空間
- 連結串列:使用連結串列的話我們需要在每個子節點中儲存其兄弟節點的連結,當我們在一個節點的子節點中查詢是否存在一個字元時,需要先找到其子節點,然後順著子節點的連結串列從左往右進行遍歷
- 優點:節省空間,有多少個子節點就佔用多少空間,不會造成空間浪費
- 缺點:對子節點進行查詢相對較慢,需要進行連結串列遍歷,同時實現也較陣列麻煩
下面是最簡單的Trie樹的實現,採用陣列的方式。
/**
* <p>
* 最簡單的Trie樹結構,僅表示出Trie樹的結構,實際應用需進行擴充套件
* </p>
*
* @author Vicky
* @email [email protected]
* @2015年11月23日
*
*/
public class Trie {
protected TrieNode root = new TrieNode('a');// TrieTree的根節點
/**
* 插入
*
* @param word
*/
public void insertWord(String word) {
TrieNode index = this.root;
for (char c : word.toLowerCase().toCharArray()) {
index = index.addChild(c);
}
return;
}
/**
* TrieTree的節點
*/
private class TrieNode {
/** 該節點的字元 */
private final char nodeChar;//
/** 一個TrieTree的節點的子節點 */
private TrieNode[] childNodes = null;
public TrieNode(char nodeChar) {
super();
this.nodeChar = nodeChar;
}
public TrieNode addChild(char ch) {
int index = ch - 'a';
if (null == childNodes) {
this.childNodes = new TrieNode[26];
}
if (null == childNodes[index]) {
childNodes[index] = new TrieNode(ch);
}
return childNodes[index];
}
@Override
public String toString() {
return "TrieNode [nodeChar=" + nodeChar + "]";
}
}
public static void main(String[] args) {
Trie trie = new Trie();
trie.insertWord("Vicky");
}
}
Trie樹應用
Trie樹的應用主要是集中於字串的處理。
(1)字串檢索
- 精確查詢:給定一組字串,查詢某個字串是否出現過
- 字首匹配:給定一組字串,查詢以某個字串為字首的字串集合
- …
(2)最長公共字首
- 查詢一組字串的最長公共字首,只需要將這組字串構建成Trie樹,然後從跟節點開始遍歷,直到出現多個節點為止(即出現分叉)
(3)排序
- 將一組字串按照字典序進行排序,只需構建成Trie樹,然後按照先序遍歷即可
。。。
下面我們針對Trie樹的應用,選擇一個相對簡單且有代表性的案例:從一組單詞中查詢所有以“vi”開頭的字串,同時查詢“Vicky”是否出現過。(由於我們只儲存26個字元,所以不區分大小寫,也不支援非單詞字元)。
package com.vicky.datastructure.tree.trie;
/**
* <p>
* 一個支援字首查詢以及精確查詢的Trie樹
* </p>
*
* @author Vicky
* @email [email protected]
* @2015年11月23日
*
*/
public class PrefixTrie {
private TrieNode root = new TrieNode('a');// TrieTree的根節點
/**
* 插入
*
* @param word
*/
public void insertWord(String word) {
TrieNode index = this.root;
for (char c : word.toLowerCase().toCharArray()) {
index = index.addChild(c);
index.addPrefixCount();
}
index.addCount();
return;
}
/**
* 查詢
*
* @param word
* @return
*/
public boolean selectWord(String word) {
TrieNode index = this.root;
for (char c : word.toLowerCase().toCharArray()) {
index = index.getChild(c);
if (null == index) {
return false;
}
}
return index.getCount() > 0;
}
/**
* 查詢前綴出現的次數
*
* @param prefix
* @return
*/
public int selectPrefixCount(String prefix) {
TrieNode index = this.root;
for (char c : prefix.toLowerCase().toCharArray()) {
index = index.getChild(c);
if (null == index) {
return 0;
}
}
return index.getPrefixCount();
}
/**
* TrieTree的節點
*/
private class TrieNode {
/** 該節點的字元 */
private final char nodeChar;//
/** 一個TrieTree的節點的子節點 */
private TrieNode[] childNodes = null;
private int count = 0;// 單詞數量,用於判斷一個單詞是否存在
private int prefixCount = 0;// 字首數量,用於查詢該前綴出現的次數
public TrieNode(char nodeChar) {
super();
this.nodeChar = nodeChar;
}
public TrieNode addChild(char ch) {
int index = ch - 'a';
if (null == childNodes) {
this.childNodes = new TrieNode[26];
}
if (null == childNodes[index]) {
childNodes[index] = new TrieNode(ch);
}
return childNodes[index];
}
public TrieNode getChild(char ch) {
int index = ch - 'a';
if (null == childNodes || null == childNodes[index]) {
return null;
}
return childNodes[index];
}
public void addCount() {
this.count++;
}
public int getCount() {
return this.count;
}
public void addPrefixCount() {
this.prefixCount++;
}
public int getPrefixCount() {
return this.prefixCount;
}
@Override
public String toString() {
return "TrieNode [nodeChar=" + nodeChar + "]";
}
}
}
package com.vicky.datastructure.tree.trie;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.regex.Pattern;
import org.junit.Before;
import org.junit.Test;
public class TrieUsedTest {
private PrefixTrie trie;
@Before
public void before() throws IOException {
Pattern pattern = Pattern.compile("[a-zA-Z]+");
// 從檔案中讀取單詞,構建TriedTree
InputStreamReader read = new InputStreamReader(this.getClass().getResourceAsStream("words.txt"));
BufferedReader reader = new BufferedReader(read);
trie = new PrefixTrie();
String line = null;
while (null != (line = reader.readLine())) {
line = line.trim();
if (!pattern.matcher(line).matches()) {// 去除非法單詞,如包含“-”
continue;
}
trie.insertWord(line);
}
}
/**
* 測試使用TriedTree搜尋前綴出現的次數
*/
@Test
public void searchPrefixWords() {
String prefix = "vi";
System.out.println(trie.selectPrefixCount(prefix));
System.out.println(trie.selectWord("Vicky"));
}
}
程式碼中為了支援精確查詢和字首查詢兩種方式,我們對TrieNode進行修改,增加了private int count = 0;private int prefixCount = 0;
兩個變數,分別用於儲存單詞出現的次數,以及前綴出現的次數。測試結果可通過使用文字編輯器進行查詢對比。
更多關於Trie樹的應用場景可閱讀參考文章1。
參考文章
相關推薦
資料結構系列——Trie樹
Trie樹,即字典樹,又稱單詞查詢樹或鍵樹,是一種樹形結構,是一種雜湊樹的變種。典型應用是用於統計和排序大量的字串(但不僅限於字串),所以經常被搜尋引擎系統用於文字詞頻統計。它的優點是:最大限度地減少無謂的字串比較,查詢效率比雜湊表高。 Trie樹結構
資料結構系列——字尾樹(附Java實現程式碼)
字尾樹,說的通俗點就是將一個字串所有的字尾按照字首樹(Trie樹,可參考此篇文章)的形式組織成一棵樹。 什麼是字尾樹 舉例:“banana\0”,其中 “\0” 作為文字結束符號,該字串所有的字尾如下: banana\0 anana\0 na
資料結構備忘錄:Trie樹基本操作
Trie樹是一種可以實現字串多模匹配的資料結構,在字串處理中有很重要的作用,本文Trie樹實現參考了殷人昆資料結構與演算法C++語言描述第二版中的內容。不同的是分支節點的分支結構用C++標準庫map容器實現,原因是map基於紅黑樹,查詢速度快,另外節省記憶體空間,避免浪費 C++實現如下: 1
資料結構系列-二叉樹的遍歷方法
今天我們來說一下二叉樹的遍歷方法,二叉樹分為先序遍歷、中序遍歷和後序遍歷。其中,先序遍歷序列和中序遍歷序列確定,就可以確定後序遍歷序列了;後序遍歷序列和中序遍歷序列確定,就可以確定先序遍歷序列了。 先序遍歷:(就記住根-左-右) 就是先訪問根節點,之後看這個二叉樹有沒有左子樹,如果有訪問左子樹的根
資料結構系列之平衡樹(DSW法構建)
1、DSW原理: DSW法構造平衡樹主要分為兩步,第一步是將一個二叉排序樹通過旋轉變為一個只含右節點的類似單鏈表的樹;第二步是通過旋轉將第一步中的樹轉化為平衡樹。 2、實現 2.1 實現第一步 <span style="font-size:18px;"&
[從頭學數學] 第255節 Python實現資料結構:字典樹(Trie)
劇情提要:阿偉看到了一本比較有趣的書,是關於《計算幾何》的,2008年由北清派出版。很好奇它裡面講了些什麼,就來看看啦。正劇開始: 星曆2016年08月03日 09:35:13, 銀河系厄爾斯星球中華帝國江南行省。 [工程師阿偉]正在和[機器小偉]一起研究[計算幾何]]。
資料結構系列(5)樹狀結構
5樹狀結構 “樹”是有一個或一個以上的節點組成,存在一個特殊的節點,稱為樹根。每個節點是一些資料和指標組合而成的記錄。除了樹根,其餘節點可分為n>=0個互斥的集合,其中每一個子集合本身也是一種樹狀結構,即此根節點的子樹。此外一棵合法的樹,節點間可以互相連
淺談資料結構-二叉樹
二叉樹是樹的特殊一種,具有如下特點:1、每個結點最多有兩顆子樹,結點的度最大為2。2、左子樹和右子樹是有順序的,次序不能顛倒。3、即使某結點只有一個子樹,也要區分左右子樹。 一、特殊的二叉樹及特點 1、斜樹 所有的結點都只有左子樹(左斜樹),或者只有右子樹(右斜樹)。這就是斜樹,應用較少
資料結構之伸展樹(二)
之前寫了一篇Splay的部落格【資料結構之伸展樹(一)】,只是說了一下了它的原理及核心的伸展操作,後來發現具體在哪裡應用splay我還是分不大清。 事實上,Splay常常用於實現可分裂與合併的序列,舉個板栗,比如給你一個數組,將陣列從某一個地方分成倆陣列,或者給你倆陣列,將他們直接連線成一個
資料結構:線段樹及ST演算法比較
ST演算法是一種高效的計算區間最值的方法。 他的思想是將詢問區間分解成兩個最長的二次冪的長度的區間並集的形式。 所以與線段樹不同,這種區間分解其實存在相交的分解。 因此ST演算法能維護的只是一些簡單的資訊,比如區間最值或者區間gcd問題 ST演算法的優勢: 實現簡單(qwq為什麼我覺得線段樹更
資料結構之 伸展樹個人筆記 伸展樹(一)之 圖文解析 和 C語言的實現
閱讀了skywang的伸展樹的講解,覺得講的很不錯,再次也推薦大家無論是新手還是老手都可以去閱讀下。 ----------------------------------------------------------------------------------------- 伸展樹(一)之 圖文
資料結構——二叉樹(程式碼)
二叉樹 C++ 環境codeblocks17 通過 /* 二叉樹 使用了自定義的 棧 和 佇列 @CGQ 2018/10/29 */ #include <iostream> #include <stdio.h> #include <stdlib.h&
資料結構之B+樹
title: 資料結構之B+樹 date: 2018-11-04 20:39:00 tags: 資料結構與演算法之美 一、 淺談B-樹索引 1.B-樹的特性 一棵m階B-樹,或者是空樹,或者是滿足以下性質的m叉樹 根結點至少有兩個分支; 除根以外的非葉結點,每個結點包含分支數範圍[[
資料結構——第二章樹和森林:04哈夫曼樹與哈夫曼編碼
1.結點的路徑長度:從根結點到該結點的路徑上分支的數目。 2.樹的路徑長度:樹中每個結點的路徑長度之和。 3.樹的帶權路徑長度:樹中所有葉子結點的帶權路徑長度之和WPL(T) = ∑wklk(對所有葉子結點) 4.最優樹:在所有含n個結點,並帶相同權值的m叉樹中,必存在一棵其帶權路徑長度取最小值的樹,稱
資料結構——3.1樹與樹的表示
一、引言 層次結構舉例 家譜、城市(鄉鎮),檔案管理系統等 為什麼用層次結構呢? 分層次組織在管理上具有更高的效率 查詢 靜態查詢:對查詢的集合沒有插入和刪除操作,只有查詢 動態查詢:對查詢的集合除查詢外,還可能發生插入和刪除 二分查詢的啟示 例如11個元素的二
資料結構 二叉樹《c++版》
二叉樹節點BinNode模板類 #define BinNodePosi(T) BinNode<T> * //節點位置 #define stature(p) ((p) ? (p)->height : -1) //節點高度(與“空樹高度為-1”的約定相統一) typedef enu
二叉樹----資料結構:二叉樹的三種遍歷,利用遞迴演算法。
二叉樹----資料結構:二叉樹的三種遍歷,利用遞迴演算法。 魯迅:總之歲月漫長,然而值得等待。 #define CHAR /* 字元型 */ /* #define INT /* 整型(二者選一) */ #
資料結構 筆記:樹的定義與操作
樹是一種非線性的資料結構 樹是由n(n>=0)個結點組成的有限集合 -如果 n = 0,成為空樹; -如果n > 0,則: ·有一個特定的稱之為根(root)的結點 ·根據點只有直接後繼,但沒有直接前驅 ·除根以外的其他結點劃分為m(m>=0)個互補相交的有
資料結構---二分搜尋樹(java實現)
樹 樹的分類 1、 二分搜尋樹 2、 平衡二叉樹: AVL;紅黑樹 3、 堆; 並查集 4、線段樹;Trie(字典樹、字首樹) 二叉樹 二叉樹具有天然的遞迴結構 每個節點的左子樹也是二叉樹 每個節點的右子樹也是二叉樹 二叉樹不一定是“滿”的 一
資料結構 筆記:樹到二叉樹的轉換
通用樹結構的回顧 -雙親孩子表示法 ·每個結點都有一個指向其雙親的指標 ·每個結點都有若干個指向其孩子的指標 另一種屬性結構模型 -孩子兄弟表示法 ·每個節點都有一個指向其第一個孩子的指標 ·每個節點都有一個指向其第一個右兄弟的指標 孩子兄弟表示法的特點 -能夠表