带父节点的平衡二叉树_数据结构(八)平衡二叉树
該來的總會來,平衡二叉樹果然又來了....
出現背景
前文已經研究過普通的二叉樹,
為什么要用二叉樹呢?因為二叉樹的結構可以實現二分法查找的效果。
你比如前文介紹的滿二叉樹:如下圖所示,
如果你想要查找4號元素,你只需要遍歷3次即可。
所以在理想情況下,二叉樹可以優化遍歷。遍歷時的時間復雜度基本上為0(logn)。但是考慮一種情況,在按順序插入數據的情況下,二叉樹會退化成鏈表結構
如下圖所示:
此時,因為是按照一定順序插入,所以最后形成的二叉樹結構是呈一種線性的,相當于一個有序的鏈表。
此時如果遍歷查詢,在時間復雜度上和鏈表是等同的(0(n))。
那如何避免二叉樹退化呢?首先考慮下不讓二叉樹退化是什么意思?也就是目的
不讓二叉樹退化是說即便是在順序插入的情況下,也不能讓其成為線性結構。這就是目的,也就是說避免二叉樹在某些情況下成為線性鏈表
那么為了實現這個目的,應該如何做呢?盡量將其調整為滿二叉樹形式或者向滿二叉樹靠近,但是滿二叉樹對每層的節點個數都有固定要求,如果單純的就是調整為滿二叉樹也不現實。
所以我們要將二叉樹盡量調整為左右子樹高度差最多不超過1的平衡二叉樹。
這也就是平衡二叉樹的作用了。
所以,接下來為了避免二叉樹的退化,我們需要明白二叉樹什么時候需要調整,要怎么調整。
也就是做兩件事
when?何時調整?判斷二叉樹是不是平衡二叉樹
how?如何調整?將非平衡的二叉樹調整為平衡二叉樹
識別平衡二叉樹
既然要將不平衡的二叉樹進行調整,那么什么樣的樹是平衡的?什么樣的樹是不平衡的?
一定要按照平衡二叉樹的性質來區分判斷:左子樹和右子樹的高度差不能超過1
如下圖所示
按照性質來判斷,看二叉樹中的每個分支節點是否平衡
圖1:三個節點中兩個葉子節點0,2,葉子節點是說沒有子節點的節點,所以葉子節點沒有左右子樹,所以葉子節點永遠是平衡的,不需要考慮。有一個分支節點1是根節點,左子樹高度為1,右子樹高度為1,高度差為0。所以是平衡二叉樹
圖2:共有四個節點,其中有兩個葉子節點0,3是平衡的不需要考慮。分支節點有1和2,需要分析其平衡性.針對節點1來說左子樹高度是1,右子樹高度是2,高度差為1,所以節點1是平衡的。針對節點2來說左子樹高度是0,右子樹高度是1,高度差是1,所以節點2是平衡的.既然除葉子節點外其他節點都是平衡的,所以圖2是平衡的。
圖3:所有節點都是平衡的,所以是平衡二叉樹。不再仔細分析,可以自己嘗試。
接下來再來分析幾個不是平衡二叉樹的二叉樹
圖4:0,5是葉子節點不需要考慮。節點1的左子樹高度為1右子樹高度為0所以為平衡節點。節點2左子樹高度為2右子樹高度為3所以為平衡節點。節點3左子樹高度為0右子樹高度為2所以為非平衡節點。節點4左子樹高度為0,右子樹高度為1,所以為平衡節點。綜上圖4不是平衡二叉樹。
所以,看一個二叉樹是不是平衡的,就看二叉樹的每個節點是不是平衡點,也就是看每個節點(葉子節點除外,不需要判斷)的左右子樹的高度差是不是小于1.
調整二叉樹
在正確的區分了二叉樹是不是平衡二叉樹后,也就是我們明確了二叉樹何時需要調整。那么接下來我們就要試著將不平衡的二叉樹調整為平衡的二叉樹。
也就是要掌握如何將非平衡的二叉樹,調整為平衡二叉樹
調整的做法不可能是增加節點或者是減少節點。而是對二叉樹進行一個旋轉的操作。
也就是說既然節點失衡,那就說明左右高度差大于1,此時可以通過旋轉來讓調整左右子樹的高度。
旋轉分為兩種,左旋和右旋。何時左旋何時右旋呢?先來看個示例
先來聲明一下,我這里的左旋和右旋意思是向左旋轉還是向右旋轉,也就是右旋表示順時針旋轉,左旋表示逆時針旋轉。而不是指代的是左子樹旋轉或是右子樹旋轉。
左旋
如下圖9所示:
圖9中,最低失衡節點為2,失衡原因是因為節點4的存在。節點4位于失衡節點的右子樹。所以對節點2進行逆時針旋轉。
節點3代替節點2的位置,節點2作為節點3的左子節點,節點3原先的左子節點(如果有子節點)作為節點2的右子節點
右旋
如圖6所示
圖6中,最低失衡節點為節點2,失衡原因是因為節點0的添加。
節點0位于節點2的左子樹,左子樹高度為2右子樹高度為0。
所以需要從左子樹移動一些節點到右子樹,也就是向右旋轉。
將失衡節點2向右旋轉,作為節點2的子節點1的子節點。
如圖7所示,旋轉后為平衡二叉樹
前邊的左旋和右旋是最基本的,簡單的操作。而且也是針對簡單的情況。
左子樹右旋中:最低失衡節點的左子樹比右子樹的高度大于1,并且最低失衡的節點只有左子樹,并且左子樹中只有左子節點,沒有右子節點
右子樹左旋中,最低失衡節點的右子樹比左子樹的高度大于1,并且最低失衡的節點中只有右子樹,右子樹中只有右子節點。
來看一些復雜的情況
左子樹失衡,失衡節點是右子節點
如圖所示
按照之前的邏輯,最低失衡節點處于其父節點的左子樹,那么就讓左子樹右旋。旋轉后如圖b所示,依舊不平衡而且問題也沒有任何的改善。這種情況下應該怎么調整呢?
為什么拿最低失衡節點的子節點代替失衡節點位置不行?因為該子節點在左子樹中的順序不是出于中間位置,因為該子節點小于父節點,也小于其右子節點。所以讓其右子節點代替父節點位置。如下圖所示
同樣,針對右子樹失衡,失衡節點是左子節點的也很類似,就不再畫出。
但前面這幾種情況,都有一個特點,那就是失衡的節點都只有一個子節點,如果有兩個子節點該如何處理?其實沒啥影響,只要找準失衡節點和失衡原因即可。
總結
在調整二叉樹時,要確認兩個東西,一個是旋轉方向,一個是旋轉軸。
旋轉方向共有兩種,順時針旋轉和逆時針旋轉,那么究竟是哪種旋轉方式由什么決定?由最低失衡節點以及左右子樹高度決定。也就是找到最低失衡節點和失衡原因。
如果節點失衡是因為左子樹高度>右子樹高度,那就說明需要把左子樹的節點旋轉到右子樹,也就是向右旋轉,即順時針旋轉
如果節點失衡是因為右子樹高度>左子樹高度,那就說明需要把右子樹節點旋轉到左子樹,也就是向左旋轉,即逆時針旋轉。
旋轉方向確定后,接下來就是確定旋轉軸了,其實也就是找到用哪個節點來代替原先的最低失衡節點。
首先,失衡原因就是左右子樹不平衡,我們需要盡量調整成以失衡節點為軸成對稱的形狀,那么也就是說我們是需要找到失衡節點所在子樹的中間位置的節點,并讓其代替失衡節點,找到中間位置后,就可以保證失衡節點所在子樹可以成為平衡樹。
其實理論上是這么說,但是實際操作起來也是挺困難的,為了更簡單的實現,我們可以盡量找某個極值,這樣只需要改變層數較多的那個子樹即可。不理解的話接著看。
查找位置有個規律:
如果造成失衡的節點位于最低失衡節點的左子樹中,那么就找到該左子樹中的最大值節點,來代替最低失衡節點。
如果造成失衡節點位于最低失衡節點的右子樹中,那么就找到該右子樹中的最小值節點,來代替最低失衡節點。
可以通過前邊兒的例子來來驗證下理論。接下來舉幾個稍微復雜的例子來驗證。
示例:
如圖所示
首先找到二叉樹的最低失衡節點為4,接下來找到二叉樹的失衡原因是節點1的存在。節點1是位于節點4的左子樹,所以是需要順時針旋轉。
接下來尋找旋轉軸,也就是包括4在內的左子樹的最大值。因為節點1是節點4的左子樹中的節點,循環遍歷左子樹,找到最大值節點為3
分析完畢后,執行旋轉過程
用節點3代替節點4的位置,也就是說節點3.parent=節點4.parent
節點3的左右子節點的確定:節點3的右子節點為4,左子節點為原先4的子節點2,調整后的二叉樹如圖b所示,但是仍舊是不平衡的,現在失衡節點為2
同理,因為失衡節點2的左子樹失衡,所以找到左子樹中的最大值,并用該節點代替2。該節點的右子節點為2,左子節點為2的左子節點0,也就是重復第二步的過程。調整后如圖c所示,成功
所以啊,只要找準最低失衡節點以及旋轉軸也就是需要替換最低失衡節點的節點,問題就迎刃而解了。
找最低失衡節點,那就是找從最底層(葉子節點層)往最高層(root層)中最先出現左右子樹高度差大于1的節點。
找替代節點就是找極值點,如果是左子樹失衡,那就找左子樹中的最大值,因為當左子樹失衡時就表示我們不需要處理右子樹,所以找到左子樹的最大值,之后把右子樹賦給該節點的右子節點即可。
同理,如果是右子樹失衡那就找右子樹中的最小值。
ok,接下來分析完畢后就可以進行代碼實現了。
但是本文還留了一個疑問,那就是如何尋找最低失衡節點。在找到最低失衡節點后,只需要找到對應的極值,并進行旋轉。但難點依舊是最低失衡節點的確定
文末彩蛋
金三銀四跳槽季,想去大廠卻又怕簡歷石沉大海??
給各位一個內推的機會,字節跳動實習生,社招方向,城市有北上廣深等等
點擊閱讀原文,可以查看職位詳情
推薦閱讀:
數據結構原理探究系列(一)
數據結構原理探究系列(二)--ArrayList
數據結構探究系列(三)--Java中自定義list
數據結構原理探究系列(四)--LinkedList
數據結構探究系列(五)--Java實現鏈表數組
數據結構原理探究系列(六)--二叉樹
數據結構探究系(七)--二叉樹實現
總結
以上是生活随笔為你收集整理的带父节点的平衡二叉树_数据结构(八)平衡二叉树的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 任正非:22年华为研发投入超1600亿元
- 下一篇: 添加到package_pycharm里面