Tre樹(字典樹)數據結構詳解(圖解)及模板

瞭解這個數據結構以前咱們須要瞭解它能被用來作什麼ios

字典樹又稱單詞查找樹,Tire樹,是一種樹形結構,是一種哈希樹的變種。典型應用是用於統計,排序和保存大量的字符串(但不只限於字符串),因此常常被搜索引擎系統用於文本詞頻統計。它的優勢是:利用字符串的公共前綴來減小查詢時間,最大限度地減小無謂的字符串比較,查詢效率比哈希樹高。web

說到底,字典樹就是用來查詢公共前綴的一個工具,延伸的話能夠用來進行串匹配,詞頻統計等,也是學AC自動機的前置技能.數組

所謂的字典樹,其實就是一個n叉樹數據結構


咱們對於每一個字母,若是有公共前綴的,咱們找到它的前綴,在後面不一樣的部分創建不一樣的子節點,好比說apple,appear,appxy 三個不一樣的單詞,公共前綴爲app,因此建樹以下:
在這裏插入圖片描述
若是下面有一個單詞爲apzl的話,建樹以下
在這裏插入圖片描述
也就是只注意前綴相同的部分,後面即便有同樣的字母也從新創建子節點app


那麼問題來了,單詞的第一個字母不止A啊,應該怎麼辦?
其實不難,學過二分圖的應該能想出來:設置一個超級源點,咱們在第一個字母上面再設一個超級源點,這樣計算的時候不考慮他就好了,這樣第一層就能夠和下面的子節點同樣創建了
如圖所示(ps:此圖有誤,apply應該爲appxy)
在這裏插入圖片描述svg


又是喜(sang)聞(xin)樂(bing)見(kuang)的代碼環節了
我的因爲ACM的緣由,就只放數組實現的板子了,(反正懂原理了指針版的也挺簡單的)工具

因爲數組不能動態開內存,因此咱們就得采用模擬的形式了,這裏其實用了一點並查集的思想,各位客官看下圖
在這裏插入圖片描述
因爲不能動態分配內存,同時字典樹又是比較耗費空間的,因此咱們的內存分配儘量大,開一個二維數組tire[maxn][26],而後tire[i][j] = k 表明編號爲i的節點的第j個孩子是編號爲k的節點,這裏的j一般指當前位的字母A-Z而後關於編號,咱們這裏的存樹方式是:若是要生成新節點,則編號++,不然編號不動,因此如上圖,APPLY的對應編號應該爲1,2,3,4,10,11;
同時有:搜索引擎

tire[1]['A'-'A'] = 2;
tire[2]['P'-'A'] = 3;
tire[3]['P'-'A']  = 10;
tire[10]['X'-'A'] = 11;
tire[11]['Y'-'A'] = 0;

這樣,查找的時候利用並查集的思想不斷向下查找便可spa

代碼以下3d

/*頭文件能夠忽略,只是一些經常使用的宏*/
#include <map>
#include <queue>
#include <cstdlib>
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <fstream>
#include <iostream>
#include <sstream>
#include <algorithm>
#define _mem(a,b) memset(a,0,(b+3)<<2)
#define fori(a) for(int i=0;i<a;i++)
#define forj(a) for(int j=0;j<a;j++)
#define ifor(a) for(int i=1;i<=a;i++)
#define jfor(a) for(int j=1;j<=a;j++)
#define mem(a,b) memset(a,b,sizeof(a))
#define IN freopen("in.txt","r",stdin)
#define OUT freopen("out.txt","w",stdout)
#define IO do{\ ios::sync_with_stdio(false);\ cin.tie(0);\ cout.tie(0);}while(0)
#define mp(a,b) make_pair(a,b);
using namespace std;
typedef long long ll;
const int maxn =  1e5;
const int INF = 0x3f3f3f3f;
const int inf = 0x3f;
const double EPS = 1e-7;
const double Pi = acos(-1);
const int MOD = 1e9+7;
int Tire[maxn][26];
char str[2000005];
bool v[maxn];
string s;
int cnt = 1;
//建樹,每輸入一個單詞到s裏面就調用_insert()就好
void _insert(){
    int root = 0;
    fori(s.size()){
        int next = s[i] - 'A';
        if(!Tire[root][next])
            Tire[root][next] = ++cnt;
        root = Tire[root][next];
    }
    v[root] = true;//這裏用了一個標記數組表示該點存在一個完整的單詞,好比說`app`和`apple`
    					 //在最後一個`p`的位置就會被標記true
}

//查找最長公共前綴
int _find(char bufs[],int leng){
    int root = 0;
    int cns = 0;
    int next;
    int res = 0;
    fori(leng){
        next = bufs[i] - 'A';
        if(Tire[root][next] == 0)
            break;
        root = Tire[root][next];
        cns++;
        if(v[root])
            res = cns;
    }
    return res;
}
相關文章
相關標籤/搜索