標題: 樹型查找 [打印本頁]
作者: 51黑tt 時間: 2016-3-5 18:33
標題: 樹型查找
折半查找所需要的,有序的、可以隨機存取的、順序結構的限制,導致了排序的額外負擔(如果是逐個添加,主要的負擔是移動數(shù)據(jù),此時是折半插入排序)。通過觀察折半查找的過程,發(fā)現(xiàn)實際上mid是從判定樹的根走到了葉子節(jié)點,而這棵判定樹和有相同節(jié)點的完全二叉樹的高度是相同的。鏈式結構的好處就是不用大量移動數(shù)據(jù),自然的用鏈樹來做查找結構應該是個好選擇。在前面我們曾經(jīng)寫過一個BSTree類,這個類大致上能滿足我們的要求。而為了在不同的輸入情況下,使得樹的高度盡可能的小,平衡樹的概念被提出來了,例如前面的AVLTree類。所謂的平衡,現(xiàn)在更多意義上是指和完全m叉樹高度的比值是小于某個常數(shù)的樹,也就是高度≤klogmn的樹,k為一個定常數(shù)。注意到AVL樹的定義實際上太苛刻,就很容易理解為什么要放寬要求。而實際能應用的平衡樹,都是為了滿足這個要求而自己定一套規(guī)則,比如B-樹,這個后面會講到。
索引有些字典會提供一個目錄,大部分情況是這樣的A……xx;B……xx;……。這樣就能迅速翻到開頭字母對應的頁數(shù)(實際上也知道了開頭字母結束的地方),并且每個頁的左右上角的單詞也說明了本頁的單詞范圍(可以判斷所查單詞在不在此頁)。這些就是索引。
使用索引能夠快速的定位查找范圍,從我們查字典的經(jīng)驗看來,這也應該是能提高查找效率的方法。注意到目錄的作用,它使得我們所需的字典空間分布,在一頁(或者幾頁)的空間上迅速的被我們得到——類比來看,如果數(shù)據(jù)太多以至內(nèi)存裝不下,我們也可以弄個“目錄”,使得可以將數(shù)據(jù)分塊的讀入內(nèi)存查找。
B-樹當數(shù)據(jù)膨脹到一個可怕的程度時,連索引都不能被全部裝入內(nèi)存——見過印刷版的EI檢索都有這個感覺,光一個檢索目錄都比我們用的字典厚。我們的辦法就是索引再索引,顯而易見的,每個索引塊都應該盡可能的大,以幫助我們獲得盡可能多的信息,而避免再次的查索引(此時一般都會涉及外存的存取)。AVL樹此時便力不從心了,我們需要一種新的結構。
相信每個學到這里的人都對B-樹的定義深惡痛絕,這個的責任應該由寫書的人來負,雖然定義、概念是人認知的重要工具和途徑,但在這里是適得其反的。原因就是B-樹根本不存在概念意義上的“概念”,它只是一個描述型的“概念”,是B-樹能夠運行從而表現(xiàn)出來的一種現(xiàn)象。不管怎么說,先看一下現(xiàn)有的書上的定義(省略了“或者為空樹”這句話):
嚴蔚敏、吳偉民《數(shù)據(jù)結構(C語言版)》
1. 每個節(jié)點至多有m棵子樹
2. 若根不是葉子節(jié)點,至少有兩棵子樹
3. 除根以外的所有非終端節(jié)點至少有ém/2ù棵子樹
4. 所有非終端節(jié)點包含下列信息數(shù)據(jù)(n,A0,K1,A1,K2,A2……,Kn,An),其中,Ki為關鍵字,且Ki<Ki+1(i=1,2,……,n-1);Ai(i=0,……,n)為指向子樹根節(jié)點的指針,且指針Ai-1所指子樹中所有節(jié)點的關鍵字均小于Ki(i=1,……,n),An所指子樹中所有的節(jié)點的關鍵字均大于Kn,n(ém/2ù-1£ n £ m-1)為關鍵字的個數(shù)(或n+1為子樹個數(shù))。
5. 所有的葉子節(jié)點都出現(xiàn)在同一層次上,并不帶信息(可以看作是外部節(jié)點或查找失敗的節(jié)點,實際上這些節(jié)點不存在,指向這些節(jié)點的指針為空)。
殷人昆等《數(shù)據(jù)結構(用面向對象方法與C++描述)》
1. m路搜索樹(實際上是上面第1、4條的內(nèi)容)
2. 根節(jié)點至少有兩個子女
3. 除根節(jié)點以外的所有節(jié)點(不包括失敗節(jié)點)至少有ém/2ù個子女
4. 所有的失敗節(jié)點都位于同一層。事實上,這些節(jié)點都是作為外部節(jié)點存在,不是樹上的節(jié)點。
非常莫名其妙的定義——憑什么根至少有兩個子女,而其他的至少ém/2ù個子女?跟AVL樹的定義比較一下就能看出這個定義的不合理處——AVL樹只是給出了平衡的定義“左右子樹高度差不超過1”,至于什么平衡因子、旋轉,根本就沒有提及,那只是為了保證平衡的手段。顯然,現(xiàn)在的B-樹的定義,把為了保證平衡而采取措施的結果包括在定義之中了,而這必將導致人的認知上的迷惑,因此這樣的定義是不合格的。或許,一個不和現(xiàn)在定義矛盾的合理的定義應該是這樣:采用如下辦法達到“平衡”的m路搜索樹……,當然,我們首先要解決什么叫“平衡”,而這個直覺上的定義前面已經(jīng)提到了。
接下來,讓我們看看保持平衡的“如下辦法”是什么。回憶一下AVL樹,那時的旋轉確實是很精妙的方法——保持中序有序的情況下改變子樹高度差,我也耗費了不少腦細胞把AVL樹的講解從書上的莫名其妙變成了按部就班,這是本人到目前為止最為得意的一件事情,到大家能看到這篇文字的時候,應該也能看見我寫的AVL樹的講解了。
兩個叉的AVL樹能旋轉,m個叉的樹怎么轉?看來得換個思路,前人已經(jīng)為我們做到了,就是節(jié)點的分裂和合并。
Ø 分裂
節(jié)點裝不下的時候就分裂,也就是說對于一個節(jié)點,當?shù)趍個元素進來的時候,就要分裂。怎么分裂?以中間元素為軸,左右兩部分各形成一個節(jié)點,作為中間元素的左右孩子——這里還是借用了二叉樹的概念,而事實上,m叉搜索樹就是多個二叉搜索樹拼合的產(chǎn)物——這樣還是中序有序的(或許不該說“中序”,大家結合二叉樹自己理解吧),中間元素帶著這兩個孩子插入到原來節(jié)點的父節(jié)點,當然,父節(jié)點滿的時候同樣要分裂,這樣一層層直到根節(jié)點(或者不用分裂)。
縱觀分裂的過程,我們才能明白前面的定義是怎么回事。如果采用如上的分裂方法,對于根節(jié)點來說,要么沒有子女,要么一分裂就兩個子女。而對于非根節(jié)點,只有當節(jié)點滿的時候才會分裂,那自然最少有ém/2ù個子女。
而“所有的失敗節(jié)點都位于同一層”是B-樹的“平衡準則”,很容易就能看出這樣的樹是“平衡”的。
所以,關于B-樹的定義實際上是B-樹維持平衡的外在表現(xiàn),它不應該作為B-樹的定義,而只能是一種描述。
Ø 合并
和二叉搜索樹一樣,刪除都歸結成在葉子節(jié)點的刪除,如果不在葉子節(jié)點,就拿葉子節(jié)點中的覆蓋掉,轉化成在葉子節(jié)點的刪除。同樣的,當不平衡出現(xiàn)時(即不滿足上面B-樹的描述),需要平衡化。
我們看到,父節(jié)點中的元素和左右兩個孩子原來可能是一個節(jié)點的,或者可以說他們可以合并成一個節(jié)點。這樣一來,如果他們的元素總和大于m-1,那么就從多的向少的撥一個元素——想想電視里老和尚手里的念珠,元素的移動過程是這樣的:元素多的節(jié)點——父節(jié)點(手里的珠子)——元素少的節(jié)點。如果他們的元素總和小于等于m-1,那么就把他們合并成一個節(jié)點,這樣一來,父節(jié)點就會少一個元素,如果也出現(xiàn)了不平衡,也同樣處理。
觀看分裂合并的過程,就會發(fā)現(xiàn)插入刪除的起點都在葉子節(jié)點,出現(xiàn)不平衡后,無論分裂還是合并,都會把多了(或者少了)一個元素的變化傳遞到父節(jié)點,從而導致對父節(jié)點的再度平衡化。和AVL樹的調(diào)整簡直是如出一轍,不同的是,AVL樹不可能分裂合并,因此采取的是旋轉的辦法;或者可以說B-樹不能旋轉,因此采取了分裂合并。而兩種調(diào)整方法都是在保持有序的情況下,使樹高(差)發(fā)生了變化。
當樹的叉多起來的時候,操作起來都會覺得不便,因此B-樹更應該是為了減少內(nèi)外存交換開銷而提出來的,和外排一樣,對一般的應用意義不大,不做系統(tǒng)級別的應用(或者是為了考試)很少會用到,就不再具體講解了。
而如果僅是在內(nèi)存中,叉的數(shù)目少一點會比較好,比如3叉(因為這樣的樹每個節(jié)點有2或3個叉,又叫2-3樹),而實際上,AVL樹(或者紅黑樹)已經(jīng)做得很好了,沒必要費心在多叉搜索樹上。
B+樹想想人們還真煩,B-樹都有人認為是Binary樹了,又來一個B+樹,看來老外還真是該打,Binary和Balance縮寫也不多保留幾個字母,都是B。
B+樹更是為了外存而提出來的,同樣的,一般用途不大,一般人不必費心了(考試的例外)。注意的是和B-樹的幾點差別:B+樹的非葉子節(jié)點都是索引,因此當刪除關鍵碼的時候,有些情況不必改動索引,查到了關鍵字也要一直走到葉子節(jié)點;另外所有的葉子節(jié)點都是鏈接在一起的,從頭到尾可以順序遍歷——和線索二叉樹很像不是嗎?
嚴版的和殷版的在B+樹的定義(更準確的說是描述)上有一個很大的分歧(節(jié)點中幾個元素幾個子樹),從教學的目的上,我同意殷版的描述,因為這樣的描述能更直接的表達B+樹是如何從B-樹演化來的,嚴版的引入了額外的區(qū)分,分散了讀者的注意力。
散列(哈希表)我更覺得哈希表是因為目前的儲存結構而誕生的。想想我們的儲存器(RAM),每個單元對應一個地址,對于數(shù)組來說,當知道首地址后,可以馬上計算出第N個元素的地址,因此可以馬上讀取。反過來說,卻不能由內(nèi)容來確定元素的地址,想知道含某個內(nèi)容的單元的地址,就必須挨個查看,或者在有序的情況下折半查找。
而當我們的儲存器能夠支持按內(nèi)容存取的話,那我們前面介紹的一切查找方法都會變得黯淡無光——想找15,馬上就能定位到15的位置,這樣O(1)的方法簡直是人們夢寐以求的。不知是幸運(前面查找的方法還是大有用武之地)還是不幸(直接的O(1)查找不可能容易的實現(xiàn)),我們想要的那種類型的儲存器(相聯(lián)存儲器)貴的要命(也可能容量根本做不大)。
既然硬件不支持,就從軟件下手吧,前面費了點口舌,是為了使大家明白現(xiàn)有的方法是建立在現(xiàn)有的結構上的,而當現(xiàn)有體系發(fā)生改變的時候,方法也會跟著改變。
顯然,只要我們能在內(nèi)容和序號之間建立一種函數(shù)關系,在現(xiàn)有儲存結構上就能實現(xiàn)按內(nèi)容存取了——內(nèi)容→函數(shù)變換→序號。
實際上,任何可列的內(nèi)容都是可以和整數(shù)一一對應的(我們總能找到這樣的法則),但是,一一對應的代價很可能是整數(shù)的范圍很廣,結果使得空間的浪費很大。打個比方,一張1901~2000年事件索引表,只需要一個100大小的數(shù)組,0元素代表1901,99元素代表2000——這里也回答了很多人疑惑的問題,2000年究竟是不是21世紀,這么一對照就會發(fā)現(xiàn)這是個歷史遺留問題,公元元年是公元1年而不是公元0年才會導致這樣的問題,歷法幾千年來修訂了好幾回,我們也不要對這個問題太較真了,一句話,隨大流。然而我們?nèi)绻鲆粋人類歷史的索引表,上下五千年(把古代人算上就是多少萬年了),但卻不是每年都有事發(fā)生(應該說是值得我們注意的事),更有甚者是確切的年份都不知道,顯然前面的做法就不太合理了,絕大多數(shù)空間都是空閑的。
因此,實際上采用的函數(shù)變換都是帶有壓縮性質(zhì)的,即輸出的值域都是有限的一個范圍,典型的就是0~表長。就像我們的日常經(jīng)驗一樣,一壓縮,就會有些元素被擠到一起,“你我不分”了。因此,還必須有合理處理“沖突”的辦法。函數(shù)變換加上“沖突”處理方法,就構成了散列這種查找方法。
具體的散列函數(shù)和沖突處理,請閱讀教科書,我不再詳細說明了。
鍵樹(Trie樹)與基數(shù)排序這其實都是建立在散列的思想上的,在他們身上,很容易就能發(fā)現(xiàn)鏈散列的影子,而Trie樹更像是靜態(tài)的基數(shù)排序。Trie樹用來組織內(nèi)存中的數(shù)據(jù)也是很好用的,并非是只適用于外存。那么一本厚書也僅僅是一兩頁,我這幾十頁的有這么一段也差不多了,^_^。具體請閱讀教科書,一般讀者可跳過。
歡迎光臨 (http://www.zg4o1577.cn/bbs/) |
Powered by Discuz! X3.1 |
主站蜘蛛池模板:
中文字幕av第一页
|
国产乱码精品一区二区三区中文
|
久综合
|
成人精品区
|
亚洲精品视频一区
|
国内精品久久久久久久影视简单
|
av喷水|
成人一区二区在线
|
欧美日韩综合一区
|
亚洲欧洲精品在线
|
精品久久一区二区三区
|
成av在线
|
精品久久久一区二区
|
婷婷中文在线
|
www九色
|
亚洲精品在线看
|
成年人在线观看
|
免费一区二区三区在线视频
|
黄色视频a级毛片
|
这里只有精品99re
|
中文字幕国产一区
|
色综合久久天天综合网
|
日本又色又爽又黄的大片
|
精品一区二区三区在线视频
|
欧美午夜精品久久久久久浪潮
|
亚洲第一网站
|
成人国产在线观看
|
日韩精品一二三
|
成人在线中文字幕
|
久久成人精品
|
黄色精品
|
丁香综合|
久久草在线视频
|
在线一区
|
亚洲精品一区二区三区蜜桃久
|
久久久精品在线
|
北条麻妃一区二区三区在线视频
|
国产精品高清在线
|
在线第一页
|
免费观看成人性生生活片
|
午夜精品在线观看
|