android搜索框功能實現_巧用 Trie 樹,實現搜索引擎關鍵詞提示功能

389de5dbf5e36b70fe35943b66e70e2c.gif1de084f0065426d35f509976d58c96c0.png來源 | 碼海責編 | Carol封圖 |?CSDN 付費下載于視覺中國我們幾乎每天都在用搜索引擎搜索信息,相信大家肯定有注意過這樣一個細節:當輸入某個字符的時候,搜索引框底下會出現多個推薦詞,如下,輸入「python」后,底下會出現挺多以python 為前綴的推薦搜索文本,它是如何實現的呢?
21bdb6e434a0067c98dc6f5ff0cdf755.png
文章標題已經給出答案了,沒錯,用 Trie 樹。本文將會從以下幾個方面來簡述一下 Trie 樹的原理,以讓大家對 Trie 樹有一個比較全面的認識。
  • 什么是 Trie 樹
  • Trie 樹的實現
  • 如何實現搜索字符串自動提示
  • 再談 Trie 樹
相信大家看了肯定有收獲

07ab667ac26582f0c919d80fb3882099.png

什么是 Trie 樹Trie 樹,又稱前綴樹,字典樹,或單詞查找樹,是一種樹形結構,也是哈希表的變種,它是一種專門處理字段串匹配的數據結構,用來解決在一組字符串集合中快速查找某個字符串的問題,主要被搜索引擎用來做文本詞頻的統計。畫重點:快速字符串匹配,詞頻統計。1、快速字符串匹配假設想要在一串字符串如 a, to, tea, ted, ten, i, in, inn 中多次查找某個字符串是否存在,該怎么做呢,很直觀的想法是用 hash,這種確實沒問題,如果 hash 函數設計得好的話,如果 hash 函數設計得不好,很容易產生沖突,進而退化成字符串間的比較,另外,在英文中其實有很多單詞有共同的前綴,比如中 tea, ted, ten 這三個單詞有共同的前綴 te, 如果用 hash 的話,無疑這些共同前綴相當于重復存了多次,比較費空間。如果用 Trie 樹的話,能解決以上兩個問題,先來看下 trie 樹是如何表示的,以以上的一組字符串 a, to, tea, ted, ten, i, in, inn 為例,它們組成的 Trie 樹如下:
46d0e82cafb1d2ea232722d1be4bf553.png
如果要查找某個字符串的話,從根節點出發,每次取待查找字符串中的一個字符往下遍歷,即可找到,可以看到它的查找時間復雜度為 O(N) (N 為字符串長度),還是很快的(英文單詞普遍比較短)。2、詞頻統計 只要在每個結點上加一個計數器,遍歷單詞時,所有字符串的最后一個字符對應結點的計算器都加 1, 如以 a,an,and 構造的 Trie 樹如下,每個結點計算器都為 1,代表以此結點存儲字符為終止字符的單詞分別為 1 個。
8affeea36200220ddcaac36e943737fa.png
從前面 Trie 樹的圖解可以看到 Trie 樹的本質就是前綴樹,通過提取出字符串的公共前綴(如果有的話),以達到快速匹配字符串的目的。通過前綴匹配,使用 Trie 樹查找字符串的效率大大提高!從以上 Trie 樹的圖解我們可以得出 Trie 樹的以下幾個特點
  1. 根節點不包含字符,除根節點外每個節點只包含一個字符
  2. 從根節點到某一節點,路徑上經過的字符連接起來,為該節點對應的字符串。如上圖中從根節點到結點 o,經過的字符為「t」和「o」,所以它表示單詞 to。
  3. 每個節點的所有子節點包含的字符都不相同,這一點也就保證了相同的前綴能夠得到復用。
那么 ?Trie 樹該怎么表示呢?

9f259e0fea3d00108dca4519997446d3.png

Trie 樹的實現從上文我們對 Trie 樹的剖析可以很明顯地看到 Trie 樹是一顆多叉樹,那么多叉樹該怎么表示呢,假設字符串都是由 26 個小寫字母組成,則顯然 Trie 樹應該是一顆 26 叉樹,每個節點包含 26 個子節點,如下
97501f11f9e6d12642a19cef0328548a.png
上圖可以看出,26 個子節點我們可以用大小為 26 的數組表示,所以 Trie 樹表示如下
/**
?*?26?個字母
?*/static?final?int?ALPHABET_SIZE?=?26;/**
?*?Trie?樹的節點表示
?*/static?class?TriedNode?{/**
?????*?根節點專用,存儲?"/"
?????*/public?char?val;/**
?????*?以此結點字符為終止字符的字符串的個數
?????*/public?int?frequency;/**
?????*?節點指向的子節點
?????*/
????TriedNode[]?children?=?new?TriedNode[ALPHABET_SIZE];public?TriedNode(char?val)?{this.val?=?val;
????}
}/**
?*?Trie?樹
?*/static?class?TrieTree?{private?TriedNode?root?=?new?TriedNode('/');?//?根節點
}
Trie 樹的表現有了,現在我們來看下 Trie 樹的兩個主要操作
  1. 根據一組字符串構造 Trie 樹
  2. 在 Trie 樹中查找字符串是否存在
先來看如何根據一組字符串構造 Trie 樹,首先如何根據一個單詞來構造 Trie 樹呢,假設我們以單詞 「and」 為例來看下 Trie 樹的表現形式
80fe1052f3c9eeaa479002772364c8f0.png
注:圖中的數字表示數組的元素位置可以看到構建 Trie 樹的主要步驟如下
  1. 構建根節點,此時根節點存有一個元素大小為 26 的數組
  2. 遍歷字符串「and」
  3. 遍歷第一個字符 a 時,將上述數組的第一個元素賦值為一個 TriedNode 實例(假設其名為 A)
  4. 當遍歷第二個字符 n 時,將 A 結點 TriedNode 數組下標為 n-a = 13 (a 的 ascii 為 97,n 的 ascii 碼為 110) 的元素賦值為一個 TriedNode 實例(假設其名為 N)
  5. 同理,當遍歷第三個字符 d 時,將 N 結點 TriedNode 數組的第 4 個元素(下標為 3)賦值為一個 TriedNode 實例(假設其名為 D),同時也將其結點的 frequency 加一,代表以此字符為終止字符的字符串多了一個。
由以上分析不難寫出根據字符串構建 Trie 樹的代碼,如下
/**
?*?Trie?樹
?*/static?class?TrieTree?{private?TriedNode?root?=?new?TriedNode('/');?//?根節點/**
?????*?以?String?為條件構建?Trie?樹
?????*?@param?s
?????*/public?void?insertString(String?s)?{
????????TriedNode?p?=?root;for?(int?i?=?0;?i?????????????char?c?=?s.charAt(i);int?index??=?c-'a';if?(p.children[index]?==?null)?{
????????????????p.children[index]?=?new?TriedNode(c);
????????????}
????????????p?=?p.children[index];//Process?char
????????}
????????p.frequency++;
????}
}
Trie 樹構造好了,再在 Trie 樹中查找某字符串是否存在就簡單很多了,遍歷字符串,查看每個字符在相應層級的數組位置的元素是否為空即可,如果是,說明不存在,如果不是,則繼續遍歷字符查找,直到遍歷完成,代碼如下
/**
?*?查找字符串是否在原字符串集合中
?*?@param?s
?*?@return?boolean
?*/public?boolean?findStr(String?s)?{
????TriedNode?p?=?root;for?(int?i?=?0;?i?????????//?當前被遍歷的字符char?c?=?s.charAt(i);int?index??=?c-'a';if?(p.children[index]?==?null)?{//?如果字符對應位置的數組元素為空,說明肯定不存在此字符,終止之后的字符遍歷return?false;
????????}//?如果存在,則繼續往后遍歷字符串
????????p?=?p.children[index];
????}return?true;
}
由于在節點中也用 frequency 保存了單詞數,所以如果在 Trie 樹中最終發現字符串存在,也可以隨便查找出此字符串的個數。

b10701dc89a5f5901c16db9f95f0d63e.png

如何實現搜索字符串自動提示功能

有了 Trie 樹,相信大家不難解決開篇的這個問題,首先搜索引擎根據用戶的搜索詞構建一顆 Trie 樹,假設這個搜索詞庫是 a, to, tea, ted, ten, i, in, inn,則構建的 Trie 樹為:
46d0e82cafb1d2ea232722d1be4bf553.png
那么當用戶在搜索框輸入「te」的時候,根據 Trie 樹的特性得知以 ?te 為前綴的字符串有 tea,ted,ten,則應該在搜索框提示詞中展示這三個字符串。這里有一個小問題,一般搜索框只會展示 10 個搜索詞,但以用戶輸入字符串為前綴的字符串可能遠超 10 次,到底該展示哪 10 個呢,最簡單的規則是展示搜索次數最多的 10 個字符串,于是問題就轉化為了 TopK 問題,維護一個有 10 個元素的小頂堆,步驟如下
  1. 先根據用戶輸入的前綴在樹中找出含有此前綴的所有字符串
  2. 我們知道在節點中保存了字符串的被搜索次數,所以利用小頂堆即可算出被搜索次數最多的 10 個字符串,即可得最終展示給用戶的提示詞。
注意:這里的求 TopK 要用是小頂堆,不是大頂堆哦,在之前一篇文章中有讀者提出了疑問,不要搞混了,小頂堆是求最大的 Top K 值,大頂堆是求最小的 TopK 值,由于我們要求最多的前 10 個搜索詞,所以應該是用小頂堆)。

這樣就解決了,考慮以下現象:我們在輸入搜索詞的時候,搜索引擎給出的提示詞可能并不是以用戶輸入的字符串為前綴的。

feecdbedfdda7935245dc891ceda2be6.png

如圖示:搜索引擎給出的搜索關鍵字并不包含有「brekfa」 前綴。

這種又是怎么實現的呢,它實際上用到了字符串編輯距離的思想,所謂字符串編輯距離是說一個字符串可以通過增刪改查字符來變成另外一個字符串。

2359308b7c99d3b4f310696663131619.png
如圖示: brekfa 添加 a 之后變成了 ?breakfa顯然所作的增刪改查次數越少,效率越高,經過最少的字符中編輯變成另一個合法的字符串后,就以此字符串為前綴去 Trie 樹中查找提示詞。當然了,像 Google 這樣的搜索引擎要實時顯示這些結果,背后肯定經過了很多改造。不過原理都大同小異。

5947d04b5d28cb12e042f1498b63f134.png

再談 Trie 樹

從前面的介紹中我們可以看到使用 Trie 樹確實在能在快速查找字符串與詞頻統計上發揮重要作用,但天下沒有免費的午餐,如果字符集比較大的話,用 Trie 樹可能會造成空間的浪費,以上文中構建的 Trie 樹為例
80fe1052f3c9eeaa479002772364c8f0.png
每個結點維護一個 26 個元素大小的數組,共有 4 個數組,也就是分配了 26 x 4 = 104 ?個元素的空間,但實際上只有三個元素空間(a,n,d)被分配了,浪費了 101 個空間,空間利率率很低,所以一般更適用于字符串前綴重復比較多的情況,當然也可以考慮對 Trie 樹進行如下縮點優化,能節省一些空間
64e434925c622b08542df59617f70bf1.png
當然這么優化后也增加了代碼的編碼難度,所以要視情況而定。另外如果用 Trie 樹的話,一般需要我們自己編碼,對工程師的編碼能力要求較高,所以是否用 Trie 樹我們一般建議如下:
  1. 如果是字符串的精確匹配查找,我們一般建議使用散列表或紅黑樹來解決,畢竟很多語言的類庫都有現成的,不需要自己實現,拿來即用

  2. 如果需要進行前綴匹配查找,則用 Trie 樹更合適一些

13b07b33b974c008ce293ece15e85bf3.png

總結

本文通過搜索引擎字符串提示簡要地概述了其實現原理,相信大家應該理解了,需要注意的是其使用場景,更推薦在需要前綴匹配查找的時候用 Trie 樹,否則像一般的精確匹配查找等更推薦用散列表和紅黑樹這些很成熟的數據結構,畢竟這兩數據結構實現一般在類庫中都是實現了的,不需要自己實現,盡量不要重復造輪子。29ebb0bfa30587d80fa4394ffed974bd.png7af65b1568e52c26c8b111d01e9c1548.png推薦閱讀
  • 手把手教你配置VS Code 遠程開發工具,工作效率提升N倍

  • 用大白話徹底搞懂HBase RowKey詳細設計

  • 后端程序員必備:書寫高質量SQL的30條建議

  • Go 遠超 Python,機器學習人才極度稀缺,全球 16,655 位程序員告訴你這些真相!

  • 任正非談“狼文化”:華為沒有 996,更沒有 007

  • 區塊鏈必讀“上鏈”哲學:“胖鏈下”與“瘦鏈上”

  • 在商業中,如何與人工智能建立共生關系?

真香,朕在看了!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/542717.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/542717.shtml
英文地址,請注明出處:http://en.pswp.cn/news/542717.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Python | 從用戶輸入數據,保存到文件,讀取并打印

Here, we have to input the data from the user, to read the data from user, we use input() method, and then the input data we have to store in the file by using write() method and then by using read() method we can get the data. 在這里,我們必須從…

python語句print type 1234的輸出結果是_Python語句 print(type(1J))的輸出結果是

【填空題】遍歷輸出文件所有行。 fopen("d:\\r2.txt","r") while True: str print(str,end) if not str: break f.close()【單選題】執行下列 Python語句將產生的結果是( ) i1 if (i): print(True) else: print( False)【單選題】Python語句 print(type(1/…

qt5.9.0調試如何查看變量的值_深入了解 Java 調試

Bug(俗稱"八阿哥") 是軟件開發繞不過的一道坎,因此調試便成了每位程序員一項必備的核心技能。調試不僅有助于理解程序的運行流程,還能改進代碼質量,最終提高開發者解決問題的能力以及交付軟件的品質。本文旨在討論 Java 調試關鍵技…

python字符串轉浮點數_Python | 打印不同的值(整數,浮點數,字符串,布爾值)...

python字符串轉浮點數In the given example, we are printing different values like integer, float, string and Boolean using print() method in python. 在給定的示例中,我們使用python中的print()方法打印不同的值,例如整數,浮點數&…

(6) 如何用Apache POI操作Excel文件-----POI-3.10的一個和注解(comment)相關的另外一個bug...

如果POI-3.10往一個工作表(sheet)里面插入數據的話,需要注意了,其有一個不太被容易發現的bug。 被插入的工作表(sheet)里面的單元格沒有包含任何的注解(comment)的時候,插…

mysql下拉刷新加載數據_下拉刷新、加載數據功能

paging nick加載更多getData();varm0,n2;//m:button點擊次數 n:一次加載幾條數據$(.page-btn-nick).click(getData);functiongetData(){$.ajax(paging.html).then(function(response){//測試url寫本頁面varobj{developer:[{name:nick},{name:ljy},{name:xzl},{name:jeson},{nam…

mcq 隊列_人工智能邏輯才能問答(MCQ)

mcq 隊列1) Why do we want to implement the concept of Logic in an AI system? So that the agent can have decision making capabilitySo that the agent can think and act humanlySo that the agent can apply the logic for finding the solution to any particular p…

第三周作業!

1、列出當前系統上所有已經登錄的用戶的用戶名,注意:同一個用戶登錄多次,則只顯示一次即可。答:本題思路:先用who命令列出當前登陸的用戶信息,然后使用cut命令對字段進行分割,選出我們需要的字段…

python導入模塊以及類_python模塊的導入以及模塊簡介

標簽: 一、模塊的定義及類型 1、定義 模塊就是用一堆的代碼實現了一些功能的代碼的集合,通常一個或者多個函數寫在一個.py文件里,而如果有些功能實現起來很復雜,那么就需要創建n個.py文件,這n個.py文件的集合就是模塊 …

mysql 指定數字排序_Mysql數據排序

排序數據普通字段排序按照單一字段排序按照多個字段排序手動指定排序順序單個字段手動排序多個字段手動排序普通字段排序按照單一字段排序排序采用order by子句,order by后面跟上排序字段,排序字段可以放多個,多個采用逗號間隔,or…

《黃帝內經 —— 央視60集紀錄片》

下載地址: http://pan.baidu.com/s/1dFI8hxf 目錄 第一部 醫史篇第1集:神奇的秘笈(《黃帝內經》是部什么書)第2集:赫赫始祖(上)(黃帝、炎帝)第3集:赫赫始祖&a…

mnist手寫數字數據集_mnist手寫數據集(1. 加載與可視化)

》》歡迎 點贊,留言,收藏加關注《《1. 模型構建的步驟:在構建AI模型時,一般有以下主要步驟:準備數據、數據預處理、劃分數據集、配置模型、訓練模型、評估優化、模型應用,如下圖所示:【注意】由…

python凱撒密碼實現_密碼:凱撒密碼及其Python實現

python凱撒密碼實現Before we start let’s some basic terminology... 在開始之前,讓我們先介紹一些基本術語... The art and science to achieve security by encoding messages to make them unreadable are known as Cryptography. That’s what the whole art…

qtextedit 默認文案_QT-純代碼控件-QSplitter(分裂器)

版權聲明:本文為博主原創文章,遵循CC 4.0 by-sa版權協議,轉載請附上原文出處鏈接和本聲明。本文鏈接:https://blog.csdn.net/qq_41488943/article/details/96431379使用Qplitter實現頁面的三布局分布1.新建一個無ui界面的工程&…

TYVJ P1030 乳草的入侵 Label:跳馬問題

背景 USACO OCT09 6TH描述 Farmer John一直努力讓他的草地充滿鮮美多汁的而又健康的牧草。可惜天不從人愿&#xff0c;他在植物大戰人類中敗下陣來。邪惡的乳草已經在他的農場的西北部份佔領了一片立足之地。草地像往常一樣&#xff0c;被分割成一個高度為Y(1 < y < 100)…

kotlin中既繼承又實現_Kotlin程序| 解決繼承中的主要沖突的示例

kotlin中既繼承又實現繼承中的主要沖突 (Overriding Conflicts in Inheritance) It may appear, we inherit more than one implementation of the same method. 看來&#xff0c;我們繼承了同一方法的多個實現。 Need to implement all the methods which we have inherited f…

python雷達圖詳解_Python簡單雷達圖繪制

import numpy as np import matplotlib.pyplot as plt import matplotlib matplotlib.rcParams[font.family] SimHei matplotlib.rcParams[font.sans-serif] [SimHei] lables np.array([綜合,KDA,發育,推進,生存,輸出]) nAttr 6 date np.array([7, 5, 6, 9, 8, 7]) angles…

瀏覽器兼容問題 透明度 position:fixed bootstrap

瀏覽器兼容問題&#xff1a;主要是ie8以下&#xff1a; 用bootstrap框架結合jq寫頁面&#xff0c;因為bootstrap有好多media和html5所以要在引入樣式后引入兩個js <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --><!-- WARNI…

s查找mysql服務_MySQL菜鳥實錄(一):MySQL服務安裝實戰

CentOS 7基本信息系統版本&#xff1a; CentOS 7.3 64bit系統配置&#xff1a; 4vCPUs | 8GB磁盤空間&#xff1a;[rootecs-ce5a-0001 ~]# df -hFilesystem Size Used Avail Use% Mounted on/dev/vda1 40G 17G 22G 44% /devtmpfs 3.9G 0 3.9G 0% /devtmpfs 3.9G 0 3.9G 0% /dev…

實驗一 線性表的順序存儲與實現_【自考】數據結構中的線性表,期末不掛科指南,第2篇

線性表這篇博客寫的是線性表相關的內容&#xff0c;包括如下部分&#xff0c;先看下有木有期待啥是線性表線性表的順序存儲線性表的基本運算在順序表上的實現線性表的鏈式存儲線性表的基本運算在單鏈表上的實現循環鏈表與雙向循環鏈表Over&#xff0c;內容還蠻多的&#xff01;…