Android技能樹 — 樹基礎知識小結(一)

前言:

如今安卓面試,對於數據結構的問題也愈來愈多了,也常常看到別人發的面試題都是問什麼紅黑樹,二叉樹查找等,因此咱們雖然不會立刻就會各類難的面試題,但起碼樹的基礎知識仍是要會的,這樣才能去進一步學。面試

貼上最近看到的一個介紹圖片: 算法

Android技能書系列:數組

Android基礎知識bash

Android技能樹 — 動畫小結數據結構

Android技能樹 — View小結post

Android技能樹 — Activity小結動畫

Android技能樹 — View事件體系小結ui

Android技能樹 — Android存儲路徑及IO操做小結spa

Android技能樹 — 多進程相關小結設計

Android技能樹 — Drawable小結

數據結構基礎知識

Android技能樹 — 數組,鏈表,散列表基礎小結

Android技能樹 — 樹基礎知識小結(一)

算法基礎知識

Android技能樹 — 排序算法基礎小結

本文主要講關於樹的基礎知識。

樹(Tree)是n(n>=0)個結點的有限集。n=0時稱爲空樹。在任意一棵非空樹中:(1)有且僅有一個特定的稱爲根(Root)的結點;(2)當n>1時,其他結點可分爲m(m>O)個互不相交的有限集T一、T二、……、 Tm,其中每個集合自己又是一棵樹,而且稱爲根的子樹(SubTree)

基礎知識

結點

根據上面的基礎知識我畫了一個歸總的圖(這樣我就不須要寫文字介紹了,啊哈哈):

樹結構特色

仍是用本身畫的圖來講明:

存儲結構

Android技能樹 — 數組,鏈表,散列表基礎小結中,咱們介紹了線性存儲,鏈式存儲,咱們的樹能夠充分用兩者來結合表示。

咱們統一來用上面各類方式來表示下面這個樹的存儲結構:

雙親表示法:

在每一個結點中,附設一個指示器指示其雙親結點在數組中的位置。

由於有十一個結點,因此咱們的index爲0-10,而後index位置中存儲(data + parent的index值),結果以下圖所示:

這裏咱們發現根據index裏面的parent指針,很容易知道父結點,可是好比我問知道了E結點,想知道它的子結點是哪幾個,就不知道了,只能經過整個結構的遍歷。

固然咱們能夠變相的 多加一個指針:

若是咱們又比較關注兄弟結點之間的關係,能夠增長一個右兄弟域來體現兄弟關係:

孩子鏈表表示法:

把每一個結點的孩子結點排列起來,以單鏈表做存儲結構,則n 個結點有n個孩子鏈表,若是是葉子結點,則此單鏈表爲空。而後n個頭指針又組成一個線性表,採用順序存儲結構,存放進一個一維數組中。

用孩子表示法表示咱們上面的樹,結構以下:

因此咱們的結點結構有二種: 1.表頭數組的表頭結點:

  1. 孩子鏈表的孩子結點:

可是這樣子對於查找某個結點的孩子,或者找某個結點的兄弟都方便,但彷佛若是要找某個結點的雙親結點就麻煩了。因此咱們能夠結合上面講過的《雙親表示法》

雙親孩子表示法:

把上面二個方法結合就能夠了。

孩子兄弟表示法:

任意一棵樹,它的結點的第一個孩子若是存在就是惟一的,它的右兄弟若是存在也是惟一的。 因此設置二個指針,分別指向該結點的第一個孩子和此結點的右兄弟

因此和上面相似,只是咱們存了不一樣的二個指針(從方法名字就知道,<孩子><兄弟>法,一個孩子,一個兄弟,二個指針)。

咱們把上面的樹按照這種方式實現:

森林:

繼續畫個圖來講明,免得打字了,嘿嘿:

分類

樹也是會根據不一樣條件,作了分類,咱們來了解一下。

那有序樹和無序數的區別在於哪裏呢?

若是將樹中結點的各子樹當作從左至右是有次序的,不能互換,則成爲有序樹,不然就是無序樹

好比咱們只是單純的表示一個家族的關係:

好比只是說明A的孩子有B跟C,這時候B和C換了位置葉 不要緊,這時候就是無序樹。

可是若是咱們這個家族譜是按照年齡來排序(長子,次子),那這時候B和C就不能換位置了,這時候就是有序樹。

可是咱們日常說的樹一般都是指的有序樹,而有序樹有不少分類,比較多的就是二叉樹。

二叉樹:

基本形態:

二叉樹性質:

其實這個相似與咱們之前數學課上要背的數學公式,你們能夠本身畫個二叉樹,而後試着上面的公式比對下,是否是正確。

遍歷二叉樹:

二叉樹的遍歷是指從根結點出發,按照某種次序依次訪問二叉樹中全部結點,使得每一個結點被訪問依次且僅被訪問一次。

前序遍歷:

單單看這個圖,其實換成我,我也看不懂規律,可是咱們只須要懂得其中的規則就行。

僞代碼:

遍歷(結點對象 t){
        if( t == null){
            return;
        }
        //第一步,實現某個業務操做,好比咱們是打印結點字符串。
        print(t)
        //第二步,遞歸方式繼續調用該方法遍歷左孩子
        遍歷(t.左孩子)
        //第三步,遞歸方式繼續調用該方法遍歷右孩子
        遍歷(t.右孩子)
}
複製代碼

咱們看到由於print在其餘方法的前面,因此叫前序遍歷。咱們如今傳入根結點到這個方法,而後依次打印而且遞歸,就會發現,就是圖上的順序。

中序遍歷:

僞代碼:

遍歷(結點對象 t){
        if( t == null){
            return;
        }
        //第一步,遞歸方式繼續調用該方法遍歷左孩子
        遍歷(t.左孩子)
        //第二步,實現某個業務操做,好比咱們是打印結點字符串。
        print(t)       
        //第三步,遞歸方式繼續調用該方法遍歷右孩子
        遍歷(t.右孩子)
}
複製代碼

咱們發現只要把咱們的打印語句放在中間,就是中序遍歷了。

後序遍歷:

僞代碼:

遍歷(結點對象 t){
        if( t == null){
            return;
        }
        //第一步,遞歸方式繼續調用該方法遍歷左孩子
        遍歷(t.左孩子)
        //第二步,遞歸方式繼續調用該方法遍歷右孩子
        遍歷(t.右孩子)
        //第三步,實現某個業務操做,好比咱們是打印結點字符串。
         print(t)       
}
複製代碼

咱們發現只要把咱們的打印語句放在最後,就是後序遍歷了。

二叉樹分類:

斜樹:

徹底二叉樹與滿二叉樹:

一棵深度爲k,且有 2^(k+1) - 1 個節點的二叉樹稱爲滿二叉樹,這種樹的特色是每一層上的節點數都是最大節點數。

而在一棵二叉樹中,除最後一層外,若其他層都是滿的,而且最後一層或者是滿的,或者是在右邊缺乏連續若干節點,則此二叉樹爲徹底二叉樹。

滿二叉樹
滿二叉樹

徹底二叉樹
徹底二叉樹

平衡二叉樹:

這塊知識不少,後期補上。

排序二叉樹:

這塊知識不少,後期補上。

線索二叉樹:

n個結點的二叉鏈表中含有n+1(2n-(n-1)=n+1)個空指針域。利用二叉鏈表中的空指針域,存放指向結點在某種遍歷次序下的前驅和後繼結點的指針(這種附加的指針稱爲"線索")。

這裏必定要說明一個知識點:什麼是前驅和後繼。

網上不少人都是對這個解釋太過於簡單以致於不少人理解錯誤,好比:

假設咱們如今有這個一個二叉樹:

我如今問 I 的前驅是誰,後繼是誰,不少人就單純的從樹的形狀上來看,也就是看 I 的上一個結點是D,因此前驅是D, I 沒有後面的子結點,因此後驅爲空。這種回答是錯誤的。咱們在 Android技能樹 — 數組,鏈表,散列表基礎小結文中提到過前驅和後繼:

好比雙向鏈表就是有前驅和後繼。那咱們單純看這棵樹是看不出來的,咱們要先把樹按照某個遍歷方式的時候,把它用這種鏈表形式擺列,而後才能知道某個結點的前驅和後繼是什麼,好比上面的圖咱們用中序遍歷,咱們獲得的是:HDIBJEAFCG,這時候 I 的前繼是D,後繼是B。

咱們在二叉樹存儲結構中,有二個指針指向它的二個子結點。

可是可能只有一個子結點,或者沒有子結點,這樣這個空的指針存儲就浪費了,咱們能夠在這個指針裏面存這個結點的前驅或者後繼結點的指針。

可是這時候又有一個問題,就是咱們不知道這個點目前到底放的是前驅的仍是左子結點的指針,因此咱們還須要一個參數來講明當前這個位置放的是哪一個的指針。

  1. 當ltag爲0的時候,說明lchild是該結點的左孩子的指針,爲1的時候說明lchild是該結點的前驅。
  2. 當rtag爲0的時候,說明rchild是該結點的右孩子的指針,爲1的時候說明rchild是該結點的後繼。

存儲結構:

二叉樹順序存儲結構:

咱們把二叉樹補充爲一個滿二叉樹,而後相應的填入一個一維數組便可。

二叉鏈表:

二叉樹每一個結點最多又二個孩子,因此爲它設計一個數據域和二個指針域。

三叉鏈表:

改進於二叉鏈表,增長父節點的指引,能更好地實現節點間的訪問

結語:

本文並無寫完,內容太多,後面再陸續補上去。哪裏寫錯了,歡迎指出。。。謝謝。

參考:

《大話數據結構》

《維基百科》

相關文章
相關標籤/搜索