1. 程式人生 > 其它 >Java開發核心知識筆記共2100頁,超通俗解析

Java開發核心知識筆記共2100頁,超通俗解析

正文

二叉樹

由 n( n > 0)個有限節點組成一個具有層次關係的集合,看起來就像一個倒掛的樹,因此稱這樣的資料結構為樹。

一個節點的子節點個數叫做度,通俗的講就是樹叉的個數。樹中最大的度叫做樹的度,也叫做階。一個 2 階樹最多有 2 個子節點即最多有 2 叉,因此這樣的樹稱為二叉樹,二叉樹是樹家族中最簡單的樹。

兩個叉的樹就是二叉樹,可這除了用來按一定結構存放資料外,跟查詢效能好像也沒關係,不會又是一個沒用的噱頭吧。

二分查詢

聽說二叉樹的原始威力來源於一種叫做二分查詢的演算法。

相傳在鸚鵡的原始社會,存在著森嚴的等級制度,每隻鳥必須按高矮順序分出等級和尊卑。

那麼問題來了,如下圖,怎樣才能找出最高、最矮、中等高的那些鸚鵡呢、以及指定高度的那隻呢?

第一種方法: 掃描法

一個一個依次測量,完畢後所有的問題都迎刃而解。

這種一個一個依次全部測量的方法叫做掃描,他的缺點很明顯,最高和最矮,需要全部測量完畢才能知曉。

而對於指定高度,最好的情況是第一次就找到;最壞的情況是最後一次才找到,時間複雜度為 n,也就是說從 13 個鸚鵡中找到指定身高的那隻,最壞的情況是查 13 次。

第二種方法:二分法

13 個鸚鵡全部聽令,按從矮到高列隊,向左看齊,報數。

報數字 1 的就是最矮的,報數字 13 的就是最高的,報數字 7 的就是中等身高的那隻。

最好和最壞的情況都是一次找到。而查詢效能一下子提高 13 倍,我的個乖乖,無論多個只鸚鵡,時間複雜度都是 1,好可怕。

問題:我不服,你這是偷換概念,有本事對比一個查詢指定高度鸚鵡的效能。

因為鸚鵡們已經按高矮排好了隊,所以指定高度的鸚鵡,要麼是站中間那個只,要麼就是在它的左邊或右邊的那群裡。

如果是中間那個,一次就找到,如果不是隻需要從中間左邊或右邊那一半中找,再在這一半中找中間那隻,對比身高。

以此類推,每次都把查詢的範圍減半,時間複雜度log2(n)。那麼 log2(13) 就是 4,最壞的情況也才 4 次,時間複雜度確實不是 1 了,但好像也不糟。

簡化如下:

問題:如果按高矮排隊,仍然需要一個一個比較,跟掃描有什麼區別,那還不如直接掃描呢?

事實確實如此,單純的一次查詢,先排序,再二分查詢,不見得比掃描快,甚至還不如。

但是,在資料的世界,大部分資料一生會被查詢無數次,如果只在資料降生的時候排一次序,往後餘生,是不是就可以直接用二分查詢,這似乎就是傳說的讀多寫少,以及對應的複用。

優點:

  • 查詢快

缺點:

  • 必須有序,需要提前排序

  • 每次查詢都需要不斷計算中間位置

二分查詢樹

如果一組資料不會或不常變更,那麼他們的位置也基本不變。可是每次查詢都需要重新計算中間位置是一種浪費,而浪費可恥。

我們能不能把所有中間節點組織起來,每次使用時,直接取中間節點?

請看下圖,找到所有單次二分查詢的中間節點,把他們連起來,並用手提起最中間的那個節點,就是一棵二分查詢樹。

優點:二分查詢樹就是通過資料結構的方式實現了二分查詢演算法,通過儲存中間節點的資料,彌補了二分查詢每次都要計算中間位置的缺點。

平衡二叉樹:

如果二分查詢樹不斷進行修改,比如刪除某些節點,經過一段時間後,最早那個中間節點的資料(根),很可能就不在中間了。

中間位置就像一個天平的支點,如果他不在中間了,那麼整個天平就會失衡,失衡的世界就會坍塌成不倫不類的瘸樹,甚至是降維成一個連結串列或者陣列。

二分查詢演算法的關鍵在於有序和中間節點,而二分查詢樹的關鍵是中間節點的維護,如果維護的節點已經不在中間了,那麼它就失去了意義。

所以必須保證「二分查詢樹」是一個正確的樹,一個根節點在中心的樹,一個左右子樹層級(高度)基本相等(高度相差不超過1)的樹,一個平衡的樹。

平衡二叉樹中最常見的就是紅黑樹:

紅黑樹規定了一系列節點顏色規則,以及對應的左旋和右旋操作來保證顏色規則,從而達到樹的平衡性。

看到這花裡胡哨的顏色以及複雜的規則,讓人第一眼就望而卻步,但所有的這些,也不過是為了保證二叉樹的平衡性,由於維持平衡的操作太過麻煩,無法用一句話簡單概括,只好用一堆人鬼難分的規則和步驟來實現,只要按著這些步驟就一定能實現二叉樹的平衡。

平衡二叉樹 = 二分查詢樹 + 平衡(左右高度相差不超過 1 )

平衡二叉樹並未提高二分查詢樹的效能,它只是保正樹不會被二向箔(多次增刪改)打擊降維成連結串列或不對稱的殘缺樹,永遠維持平衡。

另外,不僅僅是二叉樹,其他種類的樹,也是需要有序和平衡,才能發揮最大的威力。

多叉樹之 B-tree

兩個叉的樹就能折半查詢,理論可以提高一倍效能,那麼多個叉是不是能提高更多倍效能?

如下圖的 3 階(叉)樹(所有資料僅用於演示,非真實分佈)

每個節點維護兩個資料,並指向最多 3 個子節點。如圖 3 個子節點的資料分別為:小於 17, 17 ~ 35 ,大於 35。

假設,從上圖中查詢 10 這個數,步驟如下:

  1. 找到根節點,對比 10 與 17 和 35 的大小,發現 10 < 17 在左子節點,也就是第 2 層節點;

  2. 從根節點的指標,找到左子節點,對比 10 與 8 和 12 的大小,發現 8 < 10 < 12,資料在當前節點的中間子節點,也就是第 3 層節點;

  3. 通過上步節點的指標,找到中間子節點(第 3 層節點),對比 10 與 9 和 10 的大小,發現 9 < 10 == 10,因此找到當前節點的第二數即為結果。

加上忽略的 12 個數據,從 26 個數據中查詢一個數字 10,僅僅用了 log ? 3 26 \log_3 26log326 ≈ \approx≈ 3 次,而如果用平衡二叉樹,則需要 log ? 2 26 \log _2{26}log226 ≈ \approx≈ 5 次。事實證明,多叉樹確實可以再次提高查詢效能。

多叉樹是在二分查詢樹的基礎上,增加單個節點的資料儲存數量,同時增加了樹的子節點數,一次計算可以把查詢範圍縮小更多。

優點:二叉平衡樹的基礎上,使載入一次節點,可以載入更多路徑資料,同時把查詢範圍縮減到更小。

複雜節點: 至此,我們列舉的資料都是孤零零的單個數字。試想,你手裡已經有一個數據 10,為什麼還要費力吧唧的再從一堆資料中找到這個 10,自己找自己?這不是有病嗎?

單個數字只能活在演示中,現實的世界要複雜的多,我們來看一個接近真實場景的案例。

現有一個以年齡為索引的 3 階樹,儲存了一批使用者資訊,如下圖:

數字為使用者的年齡,其它為與樹排序查詢無關的業務資料,像這種索引資料與樹排序查詢無關的業務一起維護在節點的平衡多叉(階)樹稱為 B- 樹( B 樹)。

缺點:業務資料的大小可能遠遠超過了索引資料的大小,每次為了查詢對比計算,需要把資料載入到記憶體以及 CPU 快取記憶體中時,都要把索引資料和無關的業務資料全部查出來。本來一次就可以把所有索引資料載入進來,現在卻要多次才能載入完。如果所對比的節點不是所查的資料,那麼這些載入進記憶體的業務資料就毫無用處,全部拋棄。

最後

由於細節內容實在太多了,為了不影響文章的觀賞性,只截出了一部分知識點大致的介紹一下,每個小節點裡面都有更細化的內容!

小編準備了一份Java進階學習路線圖(Xmind)以及來年金三銀四必備的一份《Java面試必備指南》

本文已被CODING開源專案:【一線大廠Java面試題解析+核心總結學習筆記+最新講解視訊+實戰專案原始碼】收錄