目的
根據我在這段時間的觀察,很多人不懂一些基本的資料結構,所以今天在這裡,我不準備講Laravel的相關知識,而是來一點兒資料結構知識的科普:二叉樹。程式碼我已經上傳到了碼雲php-tree,希望大家能夠下載下來。下面的講解都是根據程式碼來的,仔細閱讀。程式碼可能不是那麼容易理解,沒關係,我在這篇博文的後面會給大家仔細的講解。
二叉樹的起源
關於到底是誰發明了二叉樹,已經無從得知了,反正歷史十分悠久,之所以二叉樹會存在,是為了解決線性搜尋耗費時間的問題,但是因為二叉樹的極端情況(有可能演變為連結串列),所以二叉樹在生產環境下基本沒有使用,但是二叉樹衍生除了許多其它的變種,比如 紅黑樹(Red-Black-Tree), 紅黑樹的應用無處不在(作業系統,標準庫等等),但是要想理解這些變種,基本的二叉樹原理,你是必須要知道的。
二叉樹的定義
二叉樹的定義很簡單,如下:
- 每一個節點,有且最多隻有2個子節點。
- 每一個節點的左子節點的值小於當前節點的值,當前節點的值小於右子節點的值。
定義很簡單,沒錯,就是這麼簡單,希望大家記住。
二叉樹的操作
今天我要講的是二叉樹的插入和刪除操作,因為這是它的核心操作,請大家仔細理解。
程式碼結構
博文的開頭,我已經給出了程式碼的地址,下載是必須的,程式碼結構如下所示:
每個檔案的作用我已經給大家標出來了,這裡我還要給大家簡要的介紹一下沒各類的基本屬性。
二叉樹的每一個節點都是Node類,這個類很簡單,如下:
各個屬性的含義,我都給大家標出來了,就不再說了,NodeComparator是一個介面,所有的結點比較器(如果比較兩個結點的大小)都應該實現NodeComparator結構,NodeComparator介面定義如下:
最後是BinaryTree類了,這個類有點兒複雜,其它的操作在本篇博文的後面,我們在這裡只看它的基礎屬性:
關於如何實現自己的結點比較器,我在test.php檔案中有給大家演示,下面我貼出來:
上面簡要的介紹了基本類的一些作用,下面開始深入二叉樹的插入和刪除操作。
二叉樹的插入
在閱讀下面的程式碼前,我希望,你已經理解了二叉樹的定義,廢話不多說。
因為插入操作不是那麼複雜,所以這裡直接貼出了所有的程式碼,
Node::create方法建立了一個Node類的物件,首先判斷根節點root_node是否為空,如果為空的話,那麼直接設定根節點就可以了,如果不為空的話,那麼就需要從根節點遍歷整棵樹,這裡提醒大家,記住二叉樹的定義,這裡所有的一切都是以這個為依據,BinaryTree的compare方法用於比較2個Node節點的大小,實現如下:
這個很簡單,不多說,compare方法比較當前結點node和將要插入的結點new_node的大小值,如果node的值大於new_node的值,也就是說new_node會插入到node的左子樹中,此時我們判斷node是否存在左子節點,判斷方法就是node->getLeftNode方法,如果存在的話,我們直接把左子節點賦值給node變數,這樣下次迭代就是以左子節點為判斷依據,也就是說下次迭代的時候,node就是當前迭代node的左子節點,如果不存在的話,那麼也就是說new_node直接插入作為node節點的左子樹。如果當前節點小於new_node的值的話,也就是說new_node會插入到當前迭代節點的右子樹中,這裡首先判斷,當前節點的右子節點是否存在,如果不存在的話,new_node節點直接作為它的當前node節點的右子節點即可,如果存在的話,那麼我們就需要繼續迭代整棵二叉樹,只不過下次迭代的node節點為當前node節點的右子節點,如此反覆,直到找到滿足條件的節點。
二叉樹的刪除
不管是二叉樹,還是它的衍生樹,刪除操作都是極其麻煩的,因為情況實在是太多了,下面我們一一分析:
刪除操作位於BinaryTree的delete方法中,如下:
在分析程式碼之前,我先給大家分析刪除的所有情況:
A. 二叉樹為空,那麼刪除操作,就直接執行完畢了。
B. 被刪除的節點沒有子節點,也就是下圖所展示的情況:
如果一個節點沒有子節點,那麼這個節點要麼是根節點,要麼是葉子節點,分別如上圖所示的情況1和情況2,這種情況很簡單。
C.被刪除的節點有且只有一個子節點,此時的所有情況如下所示:
- 情況3:被刪除的節點是根節點,並且它的左子節點是存在的。
- 情況4:被刪除節點是根節點,並且它的右子節點是存在。
- 情況5:被刪除結點不是根節點,並且它的左子節點是存在的,同時被刪除節點是它父節點的右子節點。
- 情況6:被刪除結點不是根節點,並且它的左子節點是存在的,同時被刪除節點是它父節點的左子節點。
- 情況7:被刪除結點不是根節點,並且它的右子節點是存在的,同時被刪除節點是它父節點的左子節點。
- 情況8:被刪除結點不是根節點,並且它的右子節點是存在的,同時被刪除節點是它父節點的右子節點。
D:被刪除結點有2個子節點,此時,情況有點兒複雜,如下:
- 情況9:被刪除節點是根節點,並且被刪除節點的右子節點沒有左子節點。
- 情況10:被刪除節點不是根節點,並且被刪除節點是父節點的右子節點。
- 情況11:被刪除節點不是根節點,並且被刪除節點是父節點的左子節點。
- 情況12:被刪除節點不是根節點,並且被刪除節點是父節點的右子節點,並且被刪除節點的右子節點有左子節點。
- 情況13:被刪除節點不是根節點,並且被刪除節點是父節點的左子節點,並且被刪除節點的右子節點有左子節點。
- 情況14:被刪除節點是根節點,並且被刪除節點的右子節點有左子節點。
上面我們分析了二叉樹刪除的所有情況,接下來我會根據分析情況對程式碼進行解析。
程式碼分析
我們現在分析的重點是Binarytree的delete方法,要執行刪除操作,必須先找到需要刪除的結點。
尋找刪除的結點
尋找刪除結點的過程和插入結點的過程是一樣的,只不過compare的結果為0才算是找到了目標刪除結點,簡化程式碼為:
這部分程式碼和插入是一樣的,從根節點遍歷整棵樹,直到找到compare為0的結點,相信大家如果理解了之前所說的插入,那麼這裡也應該理解了。
刪除目標節點
刪除的程式碼,我們分幾個模組分析,首先被刪除結點沒有子節點:
下面是被刪除節點只有一個子節點:
接下來是被刪除結點存在2個子節點:
程式碼的流程和上面的分析一一對應,如果你看懂了我上面的分析,程式碼肯定沒有問題,好了,測試檔案如下所示:
總結
請原諒,因為在貼出程式碼之前,我已經列舉出了二叉樹刪除的所有情況,所以後面沒有再仔細的分析程式碼,你只要看懂我上面的例項圖片,看懂程式碼,完全不是問題,如果你遇到困難,請加qq群: