1. 程式人生 > >Trie樹(字典樹)的C++實現

Trie樹(字典樹)的C++實現

問題描述:

Trie樹

又稱單詞查詢樹,是一種樹形結構,是一種雜湊樹的變種。典型應用是用於統計,排序和儲存大量的字串(但不僅限於字串),所以經常被搜尋引擎系統用於文字詞頻統計。

舉個例子:os,oh,old,char,chat這些關鍵詞構成的trie樹:

                               root

                             /         \

                          c            o

                         /               /  \

                       h              s     h

                     /  

                   a    

                 /  \

               r      t    

trie樹特點:

①根節點不包含字元,其他節點均包含一個字元

②每個節點的最大分支數為字元可能取值的個數

③每個節點對應的單詞為從根節點到該節點的路徑上的字元的排列。

④兩個單詞的公共字首為根節點到這兩個點的路徑的公共部分。

應用:

①詞頻統計:trie樹中的公共字首只由一條路徑表示,因此相對於hash方法,節約了記憶體。

②字首匹配:檢索所有以某字首開頭的字串。樸素做法的時間複雜度為O(n*N),n為字首長度,N為單詞個數,而使用trie樹可以做到O(h)h為檢索的單詞的長度。

③串排序:N個字串按字典序排序,只需trie樹進行先序遍歷

④串的快速檢索:trie樹的節點結構中可加入記錄其他資訊的變數,如某詞在文章中第一次出現的位置編號等,問題例:給出N個單片語成的熟詞表,以及一篇全用小寫英文書寫的文章,請你按最早出現的順序寫出所有不在熟詞表中的生詞。在這道題中,我們可以用字典樹,先把熟詞建一棵樹,然後讀入文章進行比較,這種方法效率是比較高的。

演算法:

樹種單詞的儲存策略有兩種:

①每個節點存一個字元

②每個節點存從根節點到該節點的路徑表示的字串

後一種方式需要的空間可能較多,但是檢索的時候不需要一個輔助棧來記錄路徑,效率較高,本著空間換時間的原則,本文采用第二種儲存方式。

程式碼實現:

測試:對單詞集word.txt構建trie樹,並給每個單詞置一個ID號(可以認為是單詞在字符集中第一次出現的位置),並檢索其中以‘fu’開頭的單詞。

//TrieTreeNode.h
#pragma once
#include<iostream>
using namespace std;


template<class T>
class TrieTreeNode
{
public:
<span style="white-space:pre">	</span>TrieTreeNode(int MaxBranch)//用於構造根節點
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>MaxBranchNum = MaxBranch;
<span style="white-space:pre">		</span>ChildNodes = new TrieTreeNode<T>*[MaxBranchNum];
<span style="white-space:pre">		</span>for (int i = 0; i < MaxBranchNum; i++)
<span style="white-space:pre">			</span>ChildNodes[i] = NULL;
<span style="white-space:pre">		</span>word = NULL;
<span style="white-space:pre">		</span>Freq = 0;
<span style="white-space:pre">		</span>ID = -1;
<span style="white-space:pre">	</span>}
public:
<span style="white-space:pre">	</span>int MaxBranchNum;//最大分支數;
<span style="white-space:pre">	</span>char* word;//單詞字串的指標
<span style="white-space:pre">	</span>TrieTreeNode<T> **ChildNodes;
<span style="white-space:pre">	</span>int Freq;//詞頻統計
<span style="white-space:pre">	</span>int ID;//構建TrieTree樹時的插入順序,可用來記錄字串第一次出現的位置
};

//TrieTree.h
#pragma once
#include<iostream>
#include"TrieTreeNode.h"
using namespace std;


template<class T>
class TrieTree
{
<span style="white-space:pre">	</span>//Insert時為節點代表的單詞word分配記憶體,Delete時只修改Freq而不刪除word,Search時以Freq的數值作為判斷依據,而不是根據word是否為NULL
public:
<span style="white-space:pre">	</span>TrieTree(const int size);
<span style="white-space:pre">	</span>~TrieTree(){ Destroy(root); };
<span style="white-space:pre">	</span>void Insert(const T* str);//插入單詞str
<span style="white-space:pre">	</span>void Insert(const T* str, const int num);//插入單詞str,帶有編號資訊
<span style="white-space:pre">	</span>int Search(const T* str);//查詢單詞str,返回出現次數
<span style="white-space:pre">	</span>bool Delete(const T* str);//刪除單詞str
<span style="white-space:pre">	</span>void PrintALL();//列印trie樹中所有節點對應的單詞
<span style="white-space:pre">	</span>void PrintPre(const T* str);//列印以str為字首的單詞
private:
<span style="white-space:pre">	</span>void Print(const TrieTreeNode<T>* p);
<span style="white-space:pre">	</span>void Destroy(TrieTreeNode<T>* p);//由解構函式呼叫,釋放以p為根節點的樹的空間
private:
<span style="white-space:pre">	</span>TrieTreeNode<T>* root;
<span style="white-space:pre">	</span>int MaxBranchNum;//最大分支數
};


template<class T>
void TrieTree<T>::Destroy(TrieTreeNode<T>* p)
{
<span style="white-space:pre">	</span>if (!p)
<span style="white-space:pre">		</span>return;
<span style="white-space:pre">	</span>for (int i = 0; i < MaxBranchNum; i++)
<span style="white-space:pre">		</span>Destroy(p->ChildNodes[i]);
<span style="white-space:pre">	</span>if (!p->word)
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>delete[] p->word;//只是釋放了char陣列word的空間,指標word本身的空間未釋放,由後續的delete p釋放
<span style="white-space:pre">		</span>p->word = NULL;
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>delete p;//釋放節點空間
<span style="white-space:pre">	</span>p = NULL;//節點指標置為空
<span style="white-space:pre">	</span>//以上的置NULL的兩句無太大意義,但是:程式設計習慣
}


template<class T>
bool TrieTree<T>::Delete(const T* str)
{
<span style="white-space:pre">	</span>TrieTreeNode<T>* p = root;
<span style="white-space:pre">	</span>if (!str)
<span style="white-space:pre">		</span>return false;
<span style="white-space:pre">	</span>for (int i = 0; str[i]; i++)
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>int index = str[i] - 'a';
<span style="white-space:pre">		</span>if (p->ChildNodes[index])
<span style="white-space:pre">			</span>p = p->ChildNodes[index];
<span style="white-space:pre">		</span>else return false;
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>p->Freq = 0;
<span style="white-space:pre">	</span>p->ID = -1;
<span style="white-space:pre">	</span>return true;
}


template<class T>
void TrieTree<T>::PrintPre(const T* str)
{
<span style="white-space:pre">	</span>TrieTreeNode<T>* p = root;
<span style="white-space:pre">	</span>if (!str)
<span style="white-space:pre">		</span>return;
<span style="white-space:pre">	</span>for (int i = 0; str[i]; i++)
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>int index = str[i] - 'a';
<span style="white-space:pre">		</span>if (p->ChildNodes[index])
<span style="white-space:pre">			</span>p = p->ChildNodes[index];
<span style="white-space:pre">		</span>else return;
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>cout << "以" << str << "為字首的單詞有:" << endl;
<span style="white-space:pre">	</span>Print(p);
}


template<class T>
int TrieTree<T>::Search(const T* str)
{
<span style="white-space:pre">	</span>TrieTreeNode<T>* p = root;
<span style="white-space:pre">	</span>if (!str)
<span style="white-space:pre">		</span>return -1;
<span style="white-space:pre">	</span>for (int i = 0; str[i]; i++)
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>int index = str[i] - 'a';
<span style="white-space:pre">		</span>if (p->ChildNodes[index])
<span style="white-space:pre">			</span>p = p->ChildNodes[index];
<span style="white-space:pre">		</span>else return 0;
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>return p->Freq;
}


template<class T>
TrieTree<T>::TrieTree(const int size)
{
<span style="white-space:pre">	</span>MaxBranchNum = size;
<span style="white-space:pre">	</span>root = new TrieTreeNode<T>(MaxBranchNum);//根節點不儲存字元
}


template<class T>
void TrieTree<T>::Insert(const T* str)
{
<span style="white-space:pre">	</span>TrieTreeNode<T>* p = root;
<span style="white-space:pre">	</span>for (int i = 0; str[i]; i++)
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>if (str[i]<'a' || str[i]>'z')
<span style="white-space:pre">		</span>{
<span style="white-space:pre">			</span>cout << "格式錯誤!" << endl;
<span style="white-space:pre">			</span>return;
<span style="white-space:pre">		</span>}
<span style="white-space:pre">		</span>int index = str[i] - 'a';//下溯的分支編號
<span style="white-space:pre">		</span>if (!p->ChildNodes[index])
<span style="white-space:pre">			</span>p->ChildNodes[index] = new TrieTreeNode<T>(MaxBranchNum);
<span style="white-space:pre">		</span>p = p->ChildNodes[index];
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>if (!p->word)//該詞以前沒有出現過
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>p->word = new char[strlen(str) + 1];
<span style="white-space:pre">		</span>strcpy_s(p->word, strlen(str) + 1, str);
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>p->Freq++;
}


template<class T>
void TrieTree<T>::Insert(const T* str, const int num)
{
<span style="white-space:pre">	</span>TrieTreeNode<T>* p = root;
<span style="white-space:pre">	</span>for (int i = 0; str[i]; i++)
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>if (str[i]<'a' || str[i]>'z')
<span style="white-space:pre">		</span>{
<span style="white-space:pre">			</span>cout << "格式錯誤!" << endl;
<span style="white-space:pre">			</span>return;
<span style="white-space:pre">		</span>}
<span style="white-space:pre">		</span>int index = str[i] - 'a';//下溯的分支編號
<span style="white-space:pre">		</span>if (!p->ChildNodes[index])
<span style="white-space:pre">			</span>p->ChildNodes[index] = new TrieTreeNode<T>(MaxBranchNum);
<span style="white-space:pre">		</span>p = p->ChildNodes[index];
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>if (!p->word)//該詞以前沒有出現過
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>p->word = new char[strlen(str) + 1];
<span style="white-space:pre">		</span>strcpy_s(p->word, strlen(str) + 1, str);
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>p->Freq++;
<span style="white-space:pre">	</span>if (num < p->ID || p->ID == -1)//取最小的num作為當前節點代表的單詞的ID
<span style="white-space:pre">		</span>p->ID = num;
}


template<class T>
void TrieTree<T>::PrintALL()
{
<span style="white-space:pre">	</span>Print(root);
}


template<class T>
void TrieTree<T>::Print(const TrieTreeNode<T>* p)
{
<span style="white-space:pre">	</span>if (p == NULL)
<span style="white-space:pre">		</span>return;
<span style="white-space:pre">	</span>if (p->Freq > 0)
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>cout << "單詞:" << p->word << "<span style="white-space:pre">	</span>頻數:" << p->Freq;
<span style="white-space:pre">		</span>if (p->ID >= 0)
<span style="white-space:pre">			</span>cout << "<span style="white-space:pre">		</span>ID:" << p->ID;
<span style="white-space:pre">		</span>cout << endl;
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>for (int i = 0; i < MaxBranchNum; i++)
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>if (p->ChildNodes[i])
<span style="white-space:pre">		</span>{
<span style="white-space:pre">			</span>Print(p->ChildNodes[i]);
<span style="white-space:pre">		</span>}
<span style="white-space:pre">	</span>}


}

//main.cpp
#pragma once
#include<iostream>
#include<fstream>
#include"TrieTree.h"
using namespace std;

void test(TrieTree<char>* t)
{
	char* charbuffer = new char[50];
	char* cb = charbuffer;

	fstream fin("d:\\words.txt");
	if (!fin){
		cout << "File open error!\n";
		return;
	}
	char c;
	int num = 0;
	while ((c = fin.get()) != EOF)
	{
		if (c >= '0'&&c <= '9')
			num = num * 10 + c - '0';
		if (c >= 'a'&&c <= 'z')
			*cb++ = c;
		if (c == '\n')
		{
			*cb = NULL;
			t->Insert(charbuffer, num);
			cb = charbuffer;
			num = 0;
		}
	}
	fin.close();
}


void main()
{
	TrieTree<char>* t = new TrieTree<char>(26);
	char* pre = "fu";
	/*char* c1 = "fuck";
	char* c2 = "class";
	char* c3 = "name";
	char* c4 = NULL;
	char* c5 = "fucka";
	char* c6 = "fuckaa";
	char* c7 = "fuckaabc";
	t->Insert(c1);
	t->Delete(c1);
	t->Insert(c1);
	t->Insert(c2);
	t->Insert(c5);
	t->Insert(c6);
	t->Insert(c7);*/
	test(t);
	t->PrintALL();
	cout << endl;
	t->PrintPre(pre);
	//cout << t->Search(c1) << endl;
	system("pause");
}

檢索結果:



相關推薦

Trie字典C++實現

問題描述: Trie樹 又稱單詞查詢樹,是一種樹形結構,是一種雜湊樹的變種。典型應用是用於統計,排序和儲存大量的字串(但不僅限於字串),所以經常被搜尋引擎系統用於文字詞頻統計。 舉個例子:os,oh,old,char,chat這些關鍵詞構成的trie樹:          

Trie字典、字首面向物件思想C++實現

Trie樹的功能、思想、實現都寫在程式碼註釋中了 使用: Trie trie = new Trie([TypeCase]); trie. TypeCase= Bit //二進位制 Number // 0-9數字 LowerCase //小寫字母

Trie字典1

stdio.h public ctu 哈希 pac 索引 cas proc ren   Trie樹。又稱字典樹,單詞查找樹或者前綴樹,是一種用於高速檢索的多叉樹結構。   Trie樹與二叉搜索樹不同,鍵不是直接保存在節點中,而是由節點在樹中的位置決定。

trie字典

arc delete png 技術分享 我只 blog 存在 紅色 style 核心思想: 利用字符串的公共前綴來降低查詢時間的開銷以達到提高效率的目的 舉個例子 上圖是由 am as tea too tooth two 構成的字典樹。每個節

Trie tree字典

pri table main radix gcc編譯器 out 字典 name dia   Trie tree有時被稱為(digital tree)或(radix tree or prefix tree)。   可能是編譯器問題,我的實現方法用gcc編譯器,debug沒問

hiho 第2周 Trie字典

oid syn one ++ tac col splay str gif 裸字典樹。AC自動機前綴技能 1 #include <set> 2 #include <map> 3 #include <queue> 4

【模板】Trie字典,單詞查詢

int n; // 0為根節點 char a[MAX_N]; // a[0] = 0; int p[MAX_N][26]; void Update(string s) { int now = 0, len = s.size(); for(register int i = 0; i < l

Trie字典:應用於統計和排序

轉載這篇關於字典樹的原因是看到騰訊面試相關的題:就是在海量資料中找出某一個數,比如2億QQ號中查找出某一個特定的QQ號。。 有人提到字典樹,我就順便了解下字典樹。 [轉自:http://blog.csdn.net/oncealong/article/details

Trie 字典

字典樹(Trie)可以儲存一些 字串->值 的對應關係。 基本上,它跟 Java 的 HashMap 功能相同,都是 key-value 對映,只不過 Trie 的 key 只能是字串。 它的優點是:最大限度地減少無謂的字串比較,查詢效率比雜湊表高。

Trie字典談到字尾

引言 常關注本blog的讀者朋友想必看過此篇文章:從 B樹、B+樹、B*樹談到R 樹 ,這次,咱們來講另外兩種樹:Tire樹與字尾樹。不過,在此之前,先來看兩個問題。 第一個問題: 一個文字檔案,大約有一萬行,每行一個詞,要求統計出其中最頻繁出現的前10個詞,請

Trie字典,字首,鍵分析詳解

Trie樹概述    Trie樹,又稱字典樹、字首樹、單詞查詢樹、鍵樹,是一種多叉樹形結構,是一種雜湊樹的變種。Trie這個術語來自於retrieval,發音為/tri:/ “tree”,也有人讀為/traɪ/ “try”。Trie樹典型應用是用於快速檢索(最

Trie字典談到字尾10.28修訂

            從Trie樹(字典樹)談到字尾樹說明:本文基本上是“整理”性質,致謝文末的參考文獻。 引言    常關注本blog的讀者朋友想必看過此篇文章:這次,咱們來講另外兩種樹:Tire樹與字尾樹。不過,在此之前,先來看兩個問題。    第一個問題: 一個文字檔

《機器學習》 周志華學習筆記第四章 決策課後習題python 實現

一、基本內容 1.基本流程 決策樹的生成過程是一個遞迴過程,有三種情形會導致遞迴返回 (1)當前節點包含的yangben全屬於同一類別,無需劃分; (2)當前屬性集為空,或是所有yangben在所有屬性上的取值相同,無法劃分; (3)當前結點包含的yangben集合為空,不能

Tire字典資料結構詳解圖解及模板

先在這裡放模板,具體圖解回去再發 #include <map> #include <queue> #include <cstdlib> #include <cm

海量資料處理之Tire字典

第一部分、Trie樹 1.1、什麼是Trie樹     Trie樹,即字典樹,又稱單詞查詢樹或鍵樹,是一種樹形結構,是一種雜湊樹的變種。典型應用是用於統計和排序大量的字串(但不僅限於字串),所以經常被搜尋引擎系統用於文字詞頻統計。它的優點是:最大限度地減少無謂的字串比較,

1015 Reversible Primes 20 分C++實現 (已AC)

題目 題目連結:https://pintia.cn/problem-sets/994805342720868352/problems/994805495863296000 思路: 這道題的思路很簡單, 檢查輸入數是否是質數, 如果是的話, 把它按進位制轉換, 再翻轉, 轉換回

1014 Waiting in Line 30 分C++實現(已AC)

題目 題目連結:https://pintia.cn/problem-sets/994805342720868352/problems/994805498207911936 思路: 建立一個視窗列表,每個列表維護一個M長度的佇列, 以及佇列末尾的人服務結束的時間, 逐個插入客戶

1012 The Best Rank 25 分c++實現(已AC)

題目: 連結:https://pintia.cn/problem-sets/994805342720868352/problems/994805502658068480 思路: 像是自己見一個數據庫, 用以增和查, 我的思路是按照4個科目(M, C, E, A)建四個表, 每

1010 Radix 25 分C++實現-終於AC了

題目 題目連結:https://pintia.cn/problem-sets/994805342720868352/problems/994805507225665536 是一道目前為止比較有意思的題,也是碰到的為數不多需要考慮上溢位的題目 知識點:整型上溢位, 二分查詢 思

1004 Counting Leaves 30 分c++實現

題目 https://pintia.cn/problem-sets/994805342720868352/problems/994805521431773184 解題思路與程式碼實現 思路1: 構建一個結構體,包含三個元素,id,是否有孩子, 層級 構建一個數組維護樹結構