字典樹和基數樹

一、字典樹

    1、概述

字典樹是一種前綴查找樹,在前綴匹配查找中應用比較多,查找樹的層級取決於字符串長度,時間複雜度O(1),但是他要求每個節點要有26各分支,所以空間開銷比較高,是一種典型的以空間換時間的數據結構。

   2、實現原理

1)、字典樹快速查找是依賴於將一個字符串分解成單個字符,然後每個字符單獨作爲一個節點,按字符串順序鏈接,到達單詞結尾時做一個結束標記。一個單詞構成一個鏈表,多個單詞時相同位置層級的節點可以合併,就能形成一個樹,這個樹就是字典樹。

字典樹
字典樹

 

如上圖,字典樹要求所有字符串共用一個根起始點,起始點爲空不存數據。到達單詞結尾點用紅色標記。

2)、結構定義

typedef struct Trie
{
    int count;  /*用於計數當前節點作爲末尾的單詞數量,同時作爲查找時判斷單詞到達末尾的標記*/
    Trie *node[26]; /*節點的26個分支*/
}Trie;

3)、實現

 1、初始化

   一棵空Trie僅包含一個根節點,該點的字符指針均指向空

Trie* TrieNodeInit()
{
    Trie* head = (Trie*)calloc(1, sizeof(Trie));
    head->count = 0;
    return head;
}

2、插入

當需要插入一個字符串S時,我們令一個指針P起初指向根節點。然後,一次掃描S中的每個字符c:
    1.當P的c字符指針指向一個已存在的節點Q,則令P=Q。
    2.當P的c字符指針指向空,則新建一個節點Q,令P的c字符指針指向Q,然後令P=Q。
    當S中的字符掃描完畢時,在當前節點P上標記它是一個字符串的末尾。

/*輸入的統一是小寫26個字母組成的字符串,這裏就不做判斷了*/
int TrieInsert(char* str, Trie* head)
{
    char* p = str;
    Trie* node = head;
    int idx = 0;

    /*循環遍歷字符串,將每個字符加入到指定節點*/
    while(*p != '\0')
    {
        /*取當前字符相對'a'字符的偏移*/
        idx = *p - 'a';

        /*node指向下一層樹節點*/
        node = node[idx];

        /*如果下一層樹爲空,則初始化該節點*/
        if(!node)
        {
           node = TrieNodeInit(); 
        }
        p++;
    }
     
    /*此時node處就是該字符串的結尾,結尾標記自增*/
    node->count++;
    return 0;
}

3、查找

當需要檢索一個字符串S在Trie中是否存在時,我們令一個指針P起初指向根節點,然後一次掃描s中的每個字符c:
    1.若P的c字符指針指向空,則說明S沒有被插入過Trie,結束檢索。
    2.若P的c字符指針指向一個已經存在的節點Q,則令P=Q。
    當S中的字符掃描完畢時,若當前節點P被標記爲一個字符串的末尾,則說明S在Trie中存在,否則說明S沒有被插入過Trie.
    可以看出在Trie中,字符數據都體現在樹的邊(指針)上,樹的節點僅保存一些額外信息。例如單詞結尾標記等。

int TrieSearch(Trie* head, const char* str)
{
    char* p = str;
    Trie* node = head;
    int idx = 0;
    while(*p != '\0')
    {
        idx = *p - 'a';
        node = node[idx];

        /*當前字符串不在樹內,直接返回0*/
        if(!node)
            return 0;
        p++;
    }
    
    /*返回當前字符串計數個數*/
    return node->count;
}

3、字典樹的應用

(1) 字符串檢索
事先將已知的一些字符串(字典)的有關信息保存到trie樹裏,查找另外一些未知字符串是否出現過或者出現頻率。

(2)文本預測

trie樹常用於搜索提示。如當輸入一個網址,可以自動搜索出可能的選擇。當沒有完全匹配的搜索結果,可以返回前綴最相似的可能。

(3)詞頻統計

統計加入字典樹內的每個單詞的計數

(4)排序

Trie樹是一棵多叉樹,只要先序遍歷整棵樹,輸出相應的字符串便是按字典序排序的結果。
比如給你N 個互不相同的僅由一個單詞構成的英文名,讓你將它們按字典序從小到大排序輸出。

(5)字符串最長公共前綴
Trie樹利用多個字符串的公共前綴來節省存儲空間,當我們把大量字符串存儲到一棵trie樹上時,我們可以快速得到某些字符串的公共前綴。

 

二、基數樹

1、概述

radix Tree(基數樹) 其實就差不多是傳統的二叉樹,只是在尋找方式上,利用比如一個unsigned int的類型的每一個比特位作爲樹節點的判斷。 可以這樣說,比如一個數1000101010101010010101010010101010,那麼按照Radix 樹的插入就是在根節點,如果遇到0,就指向左節點,如果遇到1就指向右節點,在插入過程中構造樹節點,在刪除過程中刪除樹節點
使用一個比特位判斷,會使樹的高度過高,非葉節點過多。故在實際應用中,我們一般是使用多個比特位作爲樹節點的判斷,但多比特位會使節點的子節點槽變多,增大節點的體積,一般選用2個或4個比特位作爲樹節點即可) 

基數樹構建圖
基數樹

 

2、作用

基數樹(radix tree)是將long整數與指針鍵值相關聯的機制,它存儲有效率,並且可快速查詢,用於整數值與指針的映射,對於長整型數據的映射,如何解決Hash衝突和Hash表大小的設計是一個很頭疼的問題,利用radix樹可以根據一個長整型(比如一個長ID)快速查找到其對應的對象指針。這比用hash映射來的簡單,也更節省空間,使用hash映射hash函數難以設計,不恰當的hash函數可能增大沖突,或浪費空間。

基數樹和字典樹的實現機制很像,都是將key拆分成一部分映射到樹形結構中,基數樹是將key按指針地址bit位拆分,而字典樹是將key的字符串值按字符拆分。但是基數樹的層高是相對固定的(因爲指針地址的bit位是固定的),而字典樹的高度與字符串的長度相關,而樹的高度決定了樹的空間佔用情況,所以基數樹明顯更節約空間。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章