數據結構——2-3樹


                                   2-3樹

2-3樹的概念

       前面講到了二叉搜索樹(BST)和二叉平衡樹(AVL),二叉搜索樹在最好的情況下搜索的時間復雜度為O(logn),但如果插入節點時,插入元素序列本身就是有序的,那么BST樹就退化成一個線性表了,搜索的時間復雜度為O(n)。之所以會變成O(n),是因為樹的高度變大了,BST的比較次數最大是等於樹的高度的。因此,如果想要減少比較次數,就需要降低樹的高度。在插入和刪除節點時,要保證插入節點后不能使葉子節點之間的深度之差大於1,這樣就能保證整棵樹的深度最小,這就是AVL樹解決BST搜索性能降低的策略。但由於每次插入或刪除節點后,都可能會破壞AVL的平衡,而要動態保證AVL的平衡需要很多操作,這些操作會影響整個數據結構的性能,除非是在樹的結構變化特別少的情形下,否則AVL樹平衡帶來的搜索性能提升有可能還不足為了平衡樹所帶來的性能損耗,在一些標准的集合框架中,AVL樹應用得還是比較少的,因為綜合為了平衡的性能損耗和平衡后搜索帶來的性能提升,對整個數據結構的性能提高不多。
      2-3樹也是一種平衡搜索樹,但2-3樹已經不是一棵二叉樹了,因為2-3樹允許存在3這種節點,3節點中可以存放兩個元素,並且可以有三個子節點。2-3樹的定義如下:
      2-3樹要么為空要么
      對於2節點,和普通的BST節點一樣,有一個數據域和兩個子節點指針,兩個子節點要么為空,要么也是一個2-3樹,當前節點的數據的值要大於左子樹中所有節點的數據,要小於右子樹中所有節點的數據。
      對於3節點,有兩個數據域a和b和三個子節點指針,左子樹中所有的節點數據要小於a,中子樹中所有節點數據要大於a而小於b,右子樹中所有節點數據要大於b。
     根據2-3樹的定義,2-3樹也可能會失去平衡,那么在插入和刪除節點時也是需要動態維持平衡的,但維持平衡的策略和AVL樹是不一樣的。AVL樹是通過旋轉來恢復平衡的,而2-3樹是通過節點分裂來維持的,因為2-3樹中有一些2節點,這些2節點可以變成3節點來容納插入節點,最后導致插入節點而不會讓樹失去平衡。如果2-3樹中已經沒有2節點可以分裂成3節點了呢?其實這種情況下已經可以直接插入節點了,這時候插入的位置不會導致有深度差超過1。

2-3樹插入節點

    和往BST中插入節點一樣,也要先查找一次,找出插入節點應該插入的位置。如果插入位置是一個2節點的子節點,那么就把這個2節點變成一個3節點即可。如果插入位置是一個3節點的子節點,那么這時候就不能再把3節點變成4節點了,因為2-3樹只允許2節點和3節點。假設我們把這個3節點變成了4節點,那么就需要把這個4節點進行分裂。這里會遇到多種情況,下面一一來分析:
第一種情況:如果樹只有一個3節點

往已有AB兩個元素的3節點中再插入一個C元素,如果C大於A和B,就變成了ABC的4節點了,這時候進行分裂,因為C>B>A,所以分裂成樹,其中B為根節點,A為左子節點,C為右子節點
第二種情況,如果插入的節點在一個3節點下,而且這個3節點有一個2節點的父節點

 插入節點E,把CD的3節點變成CDE的4節點,由於CDE的父節點是個2節點,因此可以把這個父節點變成3節點來增加它的容量,並且要保持BST的特性。很明顯需要從CDE中選一個元素出來,並且A節點變成一個3節點后,就有3個子節點了,CDE也要分裂要成2個節點,那么就只能選擇D來和A組成一個3節點了。這樣才能保證B小於AD,A<C<D,A<D<E。

第三種情況插入的位置已經是一個3節點,而且父節點也是一個3節點。

往3節點EF中插入I,變成EFI的4節點,這時候先把這個4節點的父節點BC變成4節點,變成4節點后就有4個子節點了,那么就需要把EFI中選擇出F加入到BC中,因為要維持BST的特性。這樣就變成了第3個圖的樣子。由於FBC的父節點是個2節點,可以進行擴容,那么就把B選擇出來加入到A節點中變成BA的3節點,變成第4個圖的樣子。


2-3樹刪除節點也和插入類似

2-3樹的查找

2-3樹查找跟BST一樣,如果查找k等於根節點中的某個值(因為根節點可能是個3節點),則找到。如果根節點是個2節點,按按BST的查找方式繼續查找,如果根節點是個3節點AB,如果k<A,則向根節點的左子樹中遞歸查找,如果A<k<B則向根節點的中子樹中遞歸查找,如果k>B,則向根節點的右子樹中遞歸查找。





注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com