Java高级工程师必备数据结构算法高效查找算法原理分析与实现
查找是在大量的信息中尋找一個(gè)特定的信息元素,在計(jì)算機(jī)應(yīng)用中,查找是常用的基本運(yùn)算,例如編譯程序中符號(hào)表的查找本文主要介紹比較難理解的樹表查找和哈希查找。
查找定義:根據(jù)給定的某個(gè)值,在查找表中確定一個(gè)其關(guān)鍵字等于給定值的數(shù)據(jù)元素(或記錄)。
查找算法分類:
1)靜態(tài)查找和動(dòng)態(tài)查找;
注:靜態(tài)或者動(dòng)態(tài)都是針對(duì)查找表而言的。動(dòng)態(tài)表指查找表中有刪除和插入操作的表。
2)無(wú)序查找和有序查找。
無(wú)序查找:被查找數(shù)列有序無(wú)序均可;
有序查找:被查找數(shù)列必須為有序數(shù)列。
平均查找長(zhǎng)度(Average Search Length,ASL):需和指定key進(jìn)行比較的關(guān)鍵字的個(gè)數(shù)的期望值,稱為查找算法在查找成功時(shí)的平均查找長(zhǎng)度。
對(duì)于含有n個(gè)數(shù)據(jù)元素的查找表,查找成功的平均查找長(zhǎng)度為:ASL = Pi*Ci的和。
Pi:查找表中第i個(gè)數(shù)據(jù)元素的概率。
Ci:找到第i個(gè)數(shù)據(jù)元素時(shí)已經(jīng)比較過的次數(shù)。
Btree創(chuàng)建過程詳細(xì)介紹
M階的Btree的幾個(gè)重要特性:
1.結(jié)點(diǎn)最多含有m顆子樹,m-1個(gè)關(guān)鍵字(m>=2);
2.除根結(jié)點(diǎn)和葉子結(jié)點(diǎn)外,其它每個(gè)結(jié)點(diǎn)至少有ceil(m / 2)個(gè)子節(jié)點(diǎn),ceil為上取整;
3.若根節(jié)點(diǎn)不是葉子節(jié)點(diǎn),則至少有兩顆子樹。
例子:
創(chuàng)建一個(gè)5階的Btree。插入的數(shù)據(jù)有:
3 14 7 1 8 5 11 17 13 6 23 12 20 26 4 16 18 24 25 19
根據(jù)Btree特性,5階則一個(gè)磁盤空間最多有5個(gè)指針(存的查找路徑 ),4個(gè)關(guān)鍵字(mysql存的數(shù)據(jù))。那么具體的插入如下所示:
插入3
插入14
插入7
插入1
插入8,此時(shí)發(fā)現(xiàn)空間不夠,這時(shí)會(huì)出現(xiàn)一個(gè)分裂,移動(dòng)中間元素到根節(jié)點(diǎn)即得到如下圖:
插入5
插入11,17
插入13:需要注意,此時(shí)Btree又會(huì)進(jìn)行一次分裂,這分裂需要注意下13會(huì)移到根節(jié)點(diǎn)
后面的插入就不一一舉例了,自己驗(yàn)證下。最后得到的這棵樹如下所示:
以上操作為Btree的詳細(xì)構(gòu)建過程,在構(gòu)建的時(shí)候主要需要注意的就是分裂,分裂后一定要判定是否符合Btree的特性,是否是一顆有序的數(shù),左邊一定小于右邊,葉子節(jié)點(diǎn)是否已經(jīng)大于階數(shù),葉子節(jié)點(diǎn)的最小數(shù)目為ceil(m(這里為5)/2)-1=2等。有時(shí)候一個(gè)數(shù)字插入的時(shí)候會(huì)進(jìn)行多次分裂操作,需要特別注意。
B+Tree和BTree類似,只是B+Tree非葉子節(jié)點(diǎn)不會(huì)存儲(chǔ)數(shù)據(jù),所有的數(shù)據(jù)都存儲(chǔ)在葉子節(jié)點(diǎn),其目的主要增加了系統(tǒng)的穩(wěn)定性。
B和B+樹的區(qū)別在于,B+樹的非葉子結(jié)點(diǎn)只包含導(dǎo)航信息,不包含實(shí)際的值,所有的葉子結(jié)點(diǎn)和相連的節(jié)點(diǎn)使用鏈表相連,便于區(qū)間查找和遍歷。
B+ 樹的優(yōu)點(diǎn)在于:
由于B+樹在內(nèi)部節(jié)點(diǎn)上不好含數(shù)據(jù)信息,因此在內(nèi)存頁(yè)中能夠存放更多的key。 數(shù)據(jù)存放的更加緊密,具有更好的空間局部性。因此訪問葉子幾點(diǎn)上關(guān)聯(lián)的數(shù)據(jù)也具有更好的緩存命中率。
B+樹的葉子結(jié)點(diǎn)都是相鏈的,因此對(duì)整棵樹的便利只需要一次線性遍歷葉子結(jié)點(diǎn)即可。而且由于數(shù)據(jù)順序排列并且相連,所以便于區(qū)間查找和搜索。而B樹則需要進(jìn)行每一層的遞歸遍歷。相鄰的元素可能在內(nèi)存中不相鄰,所以緩存命中性沒有B+樹好。
但是B樹也有優(yōu)點(diǎn),其優(yōu)點(diǎn)在于,由于B樹的每一個(gè)節(jié)點(diǎn)都包含key和value,因此經(jīng)常訪問的元素可能離根節(jié)點(diǎn)更近,因此訪問也更迅速。
哈希查找
什么是哈希表(Hash)?
我們使用一個(gè)下標(biāo)范圍比較大的數(shù)組來存儲(chǔ)元素。可以設(shè)計(jì)一個(gè)函數(shù)(哈希函數(shù), 也叫做散列函數(shù)),使得每個(gè)元素的關(guān)鍵字都與一個(gè)函數(shù)值(即數(shù)組下標(biāo))相對(duì)應(yīng),于是用這個(gè)數(shù)組單元來存儲(chǔ)這個(gè)元素;也可以簡(jiǎn)單的理解為,按照關(guān)鍵字為每一個(gè)元素"分類",然后將這個(gè)元素存儲(chǔ)在相應(yīng)"類"所對(duì)應(yīng)的地方。但是,不能夠保證每個(gè)元素的關(guān)鍵字與函數(shù)值是一一對(duì)應(yīng)的,因此極有可能出現(xiàn)對(duì)于不同的元素,卻計(jì)算出了相同的函數(shù)值,這樣就產(chǎn)生了"沖突",換句話說,就是把不同的元素分在了相同的"類"之中。后面我們將看到一種解決"沖突"的簡(jiǎn)便做法。
總的來說,"直接定址"與"解決沖突"是哈希表的兩大特點(diǎn)。
什么是哈希函數(shù)?
哈希函數(shù)的規(guī)則是:通過某種轉(zhuǎn)換關(guān)系,使關(guān)鍵字適度的分散到指定大小的的順序結(jié)構(gòu)中,越分散,則以后查找的時(shí)間復(fù)雜度越小,空間復(fù)雜度越高。
算法思想:哈希的思路很簡(jiǎn)單,如果所有的鍵都是整數(shù),那么就可以使用一個(gè)簡(jiǎn)單的無(wú)序數(shù)組來實(shí)現(xiàn):將鍵作為索引,值即為其對(duì)應(yīng)的值,這樣就可以快速訪問任意鍵的值。這是對(duì)于簡(jiǎn)單的鍵的情況,我們將其擴(kuò)展到可以處理更加復(fù)雜的類型的鍵。
算法流程:
1)用給定的哈希函數(shù)構(gòu)造哈希表;
2)根據(jù)選擇的沖突處理方法解決地址沖突;
常見的解決沖突的方法:拉鏈法和線性探測(cè)法。詳細(xì)的介紹可以參見:淺談算法和數(shù)據(jù)結(jié)構(gòu): 十一 哈希表。
3)在哈希表的基礎(chǔ)上執(zhí)行哈希查找。
哈希表是一個(gè)在時(shí)間和空間上做出權(quán)衡的經(jīng)典例子。如果沒有內(nèi)存限制,那么可以直接將鍵作為數(shù)組的索引。那么所有的查找時(shí)間復(fù)雜度為O(1);如果沒有時(shí)間限制,那么我們可以使用無(wú)序數(shù)組并進(jìn)行順序查找,這樣只需要很少的內(nèi)存。哈希表使用了適度的時(shí)間和空間來在這兩個(gè)極端之間找到了平衡。只需要調(diào)整哈希函數(shù)算法即可在時(shí)間和空間上做出取舍。
復(fù)雜度分析:
單純論查找復(fù)雜度:對(duì)于無(wú)沖突的Hash表而言,查找復(fù)雜度為O(1)(注意,在查找之前我們需要構(gòu)建相應(yīng)的Hash表)。
使用Hash
我們?cè)趯?shí)際編程中存儲(chǔ)一個(gè)大規(guī)模的數(shù)據(jù),最先想到的存儲(chǔ)結(jié)構(gòu)可能就是map,也就是我們常說的KV pair,經(jīng)常使用Python的博友可能更有這種體會(huì)。使用map的好處就是,我們?cè)诤罄m(xù)處理數(shù)據(jù)處理時(shí),可以根據(jù)數(shù)據(jù)的key快速的查找到對(duì)應(yīng)的value值。map的本質(zhì)就是Hash表,那我們?cè)讷@取了超高查找效率的基礎(chǔ)上,我們付出了什么?
Hash是一種典型以空間換時(shí)間的算法,比如原來一個(gè)長(zhǎng)度為100的數(shù)組,對(duì)其查找,只需要遍歷且匹配相應(yīng)記錄即可,從空間復(fù)雜度上來看,假如數(shù)組存儲(chǔ)的是byte類型數(shù)據(jù),那么該數(shù)組占用100byte空間。現(xiàn)在我們采用Hash算法,我們前面說的Hash必須有一個(gè)規(guī)則,約束鍵與存儲(chǔ)位置的關(guān)系,那么就需要一個(gè)固定長(zhǎng)度的hash表,此時(shí),仍然是100byte的數(shù)組,假設(shè)我們需要的100byte用來記錄鍵與位置的關(guān)系,那么總的空間為200byte,而且用于記錄規(guī)則的表大小會(huì)根據(jù)規(guī)則,大小可能是不定的。
歡迎關(guān)注轉(zhuǎn)發(fā)評(píng)論~~
Btree創(chuàng)建過程詳細(xì)介紹圖文演示效果不是很好,我錄制好有圖文加視頻配合講解更容易理解需要的請(qǐng)加QQ群938837867暗號(hào)回復(fù)“555”贈(zèng)送一些Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式資料
總結(jié)
以上是生活随笔為你收集整理的Java高级工程师必备数据结构算法高效查找算法原理分析与实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 云监控服务产品优势与应用场景
- 下一篇: Java《剑指Offer》面试题2:替换