1. 程式人生 > >【天天數據結構和算法】PHP中trie數據結構的使用場景和代碼實例

【天天數據結構和算法】PHP中trie數據結構的使用場景和代碼實例

var_dump count ret get ech 進行 文件內容 tro 第一個

一、trie介紹

Trie樹,又稱字典樹,單詞查找樹或者前綴樹,是一種用於快速檢索的多叉樹結構,如英文字母的字典樹是一個26叉樹,數字的字典樹是一個10叉樹。

Trie一詞來自retrieve,發音為/tri:/ “tree”,也有人讀為/tra?/ “try”。

Trie樹可以利用字符串的公共前綴來節約存儲空間。如下圖所示,該trie樹用10個節點保存了6個字符串tea,ten,to,in,inn,int。

技術分享

在該trie樹中,字符串in,inn和int的公共前綴是“in”,因此可以只存儲一份“in”以節省空間。當然,如果系統中存在大量字符串且這些字符串基本沒有公共前綴,則相應的trie樹將非常消耗內存,這也是trie樹的一個缺點。

Trie樹的基本性質可以歸納為:

(1)根節點不包含字符,除根節點意外每個節點只包含一個字符。

(2)從根節點到某一個節點,路徑上經過的字符連接起來,為該節點對應的字符串。

(3)每個節點的所有子節點包含的字符串不相同。

二、trie的優點

1.查找或匹配字符串時 時間復雜度只和樹的深度有關,和節點數量無關。

2.所以,在查找海量數據,或匹配數據,或過濾數據中有很好的實現。

三、php代碼實現

Trie.php

<?php

/**
 * Created by PhpStorm.
 * User: jysdhr
 * Date: 2017/7/4
 * Time: 9:57
 * Description:PHP實現trie字典數據結構
 
*/ include "TrieNode.php"; class Trie { private $root; public function __construct() { $this->root = new TrieNode(); } public function foreach_trie() { echo "<pre>"; print_r($this->root); } public function insert($str) {
$this->__insert($this->root, $str); } public function search(string $str): bool { return $this->__search($this->root, $str); } private function __insert(&$node, $str) { if (strlen($str) == 0) return; //第一個字符,插入哪一個分叉 $k = ord(substr($str, 0, 1)) - ord(‘a‘); if (!isset($node->childs[$k]) || $node->childs[$k] == NULL) { //如果分叉不存在則重新開辟分叉 $node->childs[$k] = new TrieNode(); //記錄字符 $node->childs[$k]->nodeChar = $k; $node->childs[$k]->is_end = strlen($str) == 1 ? true : false; } $nextWord = substr($str, 1); $this->__insert($node->childs[$k], $nextWord); } /** * @Description:查找str是否存在樹中 * @User:jysdhr */ private function __search($node, $str) { if (strlen($str) == 0) return false; //首先對str進行拆分 $k = ord(substr($str, 0, 1)) - ord(‘a‘); if (isset($node->childs[$k])) { $nextWord = substr($str, 1); if (strlen($str) == 1) { //匹配最後一個字符 if ($node->childs[$k]->is_end) return true; } return $this->__search($node->childs[$k], $nextWord); } return false; } }

TrieNode.php

<?php

/**
 * Created by PhpStorm.
 * User: jysdhr
 * Date: 2017/7/4
 * Time: 10:01
 * Description:
 */
class TrieNode
{
    public  $nodeChar,$childs,$is_end;
    public function __construct()
    {
        $this->childs = array();
    }
}

testTrie.php

<?php
// 測試文件demo.php
include "Trie.php";
$str = file_get_contents(‘bbe.txt‘);//將整個文件內容讀入到一個字符串中
$badword = explode(" ", $str);//轉換成數組
$trie = new Trie();
foreach ($badword as $word)
    $trie->insert($word);

// array_combine() 函數通過合並兩個數組來創建一個新數組,其中的一個數組是鍵名,另一個數組的值為鍵值。如果其中一個數組為空,或者兩個數組的元素個數不同,則該函數返回 false。
// array_fill() 函數用給定的值填充數組,返回的數組有 number 個元素,值為 value。返回的數組使用數字索引,從 start 位置開始並遞增。如果 number 為 0 或小於 0,就會出錯。
//$badword1 = array_combine($badword,array_fill(0,count($badword),‘*‘));

$test_str = ‘knowledgeasdad‘;
$start_time = microtime(true);

var_dump(in_array($test_str,$badword));
$end_time = microtime(true);
echo ($end_time-$start_time).‘</br>‘;

$start_time1 = microtime(true);
var_dump($trie->search($test_str));
$end_time1 = microtime(true);

echo ($end_time1-$start_time1).‘</br>‘;

$start_time2 = microtime(true);
foreach ($badword as $value){
    if ($value == $test_str){
        echo ‘1‘;
        break;
    }
}
$end_time2 = microtime(true);

echo ($end_time2-$start_time2).‘</br>‘;

?>

以上是匹配聖經中的一個單詞的實例,in_array是遍歷匹配 ,當匹配單詞越晚出現,耗時越久,當匹配單詞不存在時,耗時不可接受。

trie的表現比較穩定,無論是否存在,或匹配單詞出現的早晚都與運行時間無太大影響,只與單詞長度有關。(效果最好,最穩定)

遍歷查找基本與in_array效果相當。

四、總結

所以在項目開發中,在做到敏感詞屏蔽,統計詞頻,字典等功能時,可以考慮運用trie數據結構生成一個trie樹序列化存儲起來,待更新詞庫時重新維護trie樹,是不是覺得也不是很困難呢,加油。

【天天數據結構和算法】PHP中trie數據結構的使用場景和代碼實例