數據結構(一)--樹

樹的定義

什麼是樹 :一棵樹是一些節點的集合。這個集合可以是空集;若不是空集,則樹由稱作的節點r以及0或多個非空的子樹組成,這些子樹中每一顆的根都被來自根r的一條有向的所連接。也就是說,一棵樹是N個節點和N-1條邊的集合。

一些名字解釋
兒子:節點的子樹的根叫做該節點的兒子;
父親:該節點就是兒子的父親;
樹葉:沒有兒子的節點稱作樹葉;
兄弟:具有相同父親的節點叫做兄弟;
深度:從節點n到根r的長度爲深度,r的深度爲0;
高度:從節點n到樹葉的最長長度爲高度,所有樹葉的高度爲0;一棵樹的高度就是根的高度;

先、中、後序遍歷
先、中、後指的是操作根節點的順序,左節點先於右節點操作,所以先、中、後序遍歷的執行順序分別爲:根-左-右、左-根-右、左-右-根。
在這裏插入圖片描述

二叉查找樹

二叉樹是在樹的基礎上限定了兒子的個數小於等於2而得到的。而對於二叉查找樹,就是在二叉樹的定義上在加一個限制:對於任意節點X,它的左子樹的所有值都小於X,它的右子樹的所有值都大於X。
二叉查找樹的java實現
在這裏插入圖片描述

AVL樹

爲了防止二叉查找樹退化爲鏈表,我們需要再加一些限制:平衡
AVL(Adelson-Velskii和Landis)樹帶有平衡條件:對於任意節點,它的左子樹和右子樹的高度差最多爲1(我們定義空樹的高度爲-1)
平衡條件防止二叉樹退化爲鏈表,但是也給插入和刪除增加了實現難度。以插入爲例,根據不同的插入情況,可以分爲四種類型:

  1. 對a節點的左兒子的左子樹插入 LL
  2. 對a節點的左兒子的右子樹插入 LR
  3. 對a節點的右兒子的左子樹插入 RL
  4. 對a節點的右兒子的右子樹插入 RR
    上述a節點表示第一個出現不平衡的節點,第一表示從樹葉開始算(要不然肯定都是根節點)。
    對於1和4,插入的情況放生在兩邊,通過一次單旋轉可以解決平衡問題;2和3發生在中間,需要雙旋轉(其實就是兩次單旋轉)來解決平衡問題。

單旋轉

以RR爲例,如下圖
一次左旋轉節點6爲新插入的節點,插入後,2節點的左子樹高度爲1,右子樹高度爲3,不平衡。6插入的是2節點的右兒子的右子樹,所以屬於RR情況。對於RR情況,需要在a節點(例子中的2節點)和它的右兒子(4節點)之間進行一次左旋轉,所謂左旋轉就是:父節點旋轉爲右兒子的兒子。在這個過程中,如果右兒子有左兒子,則該左兒子就是原父節點的新右兒子。

以圖中例子總結左旋轉過程:

  1. 設置2節點的右兒子爲4的左兒子3
  2. 設置右兒子4的左兒子爲原父節點2

需要注意的是,上述例子中6節點插入到了5節點的右兒子,這個不是成爲RR情況原因,6節點插入到5節點的左兒子也是屬於RR,請注意四種情況的定義,找到a節點,以它爲出發點判斷失衡情況。

LL的右旋轉請大家自己嘗試,有問題可以留言討論

雙旋轉

現在大家已經明白左右旋轉了,現在結合兩個旋轉來完成雙旋轉。以下圖爲例
在這裏插入圖片描述如下圖,節點5插入到了2節點的右兒子的左子樹中,所以屬於RL。我們需要從樹葉到根進行先後兩次單旋轉,右旋轉和左旋轉。

需要注意的是,一:從樹葉到根的順序;二:旋轉順序,RL就是先右後左,LR就是先左後右。

java實現

伸展樹

提出伸展樹的背景:

  1. 對於二叉查找樹(不是AVL樹),訪問一個節點的時間複雜度爲O(logN),最壞情況是O(N),如果經常出現最壞情況,比如一直訪問同一個節點,此時耗費的時間會比較大
  2. 一個節點被訪問後,該節點和訪問過程中路過的節點的訪問可能性會增大。

在這樣的背景下,伸展樹的用途就是可以將被訪問到的節點推到根節點,而且訪問途中的節點深度不會很大(即訪問時間也比較小)。

展開

因爲我們的目的是要把目標X節點推到根部,所以需要沿着訪問路徑通過某些操作將X一步一步的推到根部。我們可能最先想到的就是使用旋轉,每次讓X和它的父節點旋轉,這樣到最後X就會出現到根部。但是這樣的操作之後會造成沿途節點在一個比較深的位置。所以我們通過以下兩種情況來分別處理。討論之前先明確一點,目標X存在祖父節點(如果只有父節點就直接和父節點旋轉)。

之子型

在這裏插入圖片描述如圖,類似於LR的情況(RL類似),我們就進行一次雙旋轉

一字型

在這裏插入圖片描述如圖情況,則按照圖中規則操作。

在這裏插入圖片描述

java中樹的應用

java中常見的比如TreeSet、TreeMap以及HashMap中鏈表過深轉換的樹形結構。這些樹都屬於紅黑樹,在後面寫到紅黑樹的時候會詳細解讀這裏。

以上~ . ~
感謝5.1還在學習的我們

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章