1. 程式人生 > >4、樹和二叉樹

4、樹和二叉樹

1、樹和二叉樹的一些基本知識點

1.1、樹

一種非線性結構。樹是遞迴結構,在樹的定義中又用到了樹的概念。

1.1.1、樹的基本術語:

  1. 樹結點:包含一個數據元素及若干指向子樹的分支;
  2. 孩子結點:結點的子樹的根稱為該結點的孩子;
  3. 雙親結點:B結點是A結點的孩子,則A結點是B結點的雙親;
  4. 兄弟結點:同一雙親的孩子結點;
  5. 堂兄結點:同一層上結點;
  6. 結點層次:根結點的層定義為1;根的孩子為第二層結點,依此類推;
  7. 樹的高(深)度:樹中最大的結點層
  8. 結點的度:結點子樹的個數
  9. 樹的度: 樹中最大的結點度。
  10. 葉子結點:也叫終端結點,是度為0的結點;
  11. 分枝結點:度不為0的結點(非終端結點);
  12. 森林:互不相交的樹集合;
  13. 有序樹:子樹有序的樹,如:家族樹;
  14. 無序樹:不考慮子樹的順序;

1.1.3、樹的種類

一、無序樹:樹中任意節點的子節點之間沒有順序關係,這種樹稱為無序樹,也稱為自由樹; 二、有序樹:樹中任意節點的子節點之間有順序關係,這種樹稱為有序樹;

  1. 二叉樹:每個節點最多含有兩個子樹的樹稱為二叉樹; a、完全二叉樹:對於一顆二叉樹,假設其深度為d(d>1)。除了第d層外,其它各層的節點數目均已達最大值,且第d層所有節點從左向右連續地緊密排列,這樣的二叉樹被稱為完全二叉樹,其中滿二叉樹的定義是所有葉節點都在最底層的完全二叉樹;
完全二叉樹

b、平衡二叉樹(AVL樹):當且僅當任何節點的兩棵子樹的高度差不大於1的二叉樹; c、排序二叉樹

(二叉查詢樹(英語:Binary Search Tree),也稱二叉搜尋樹、有序二叉樹); 2. 霍夫曼樹(用於資訊編碼):帶權路徑最短的二叉樹稱為哈夫曼樹或最優二叉樹; 3. B樹:一種對讀寫操作進行優化的自平衡的二叉查詢樹,能夠保持資料有序,擁有多餘兩個子樹。

1.2、二叉樹

二叉樹可以為空。二叉樹結點的子樹要區分左子樹和右子樹,即使只有一棵子樹也要進行區分,說明它是左子樹,還是右子樹。這是二叉樹與樹的最主要的差別。注意區分:二叉樹、二叉查詢樹/二叉排序樹/二叉搜尋樹、二叉平衡(查詢)樹

二叉平衡樹肯定是一顆二叉排序樹堆不是一顆二叉平衡樹

二叉樹與樹是不同的,二叉樹不等價於分支樹最多為二的有序樹

。當一個結點只包含一個子節點時,對於有序樹並無左右孩子之分,而對於二叉樹來說依然有左右孩子之分,所以二叉樹與樹是兩種不同的結構

1.2.1、二叉樹重要性質

見參考文獻4《大話資料結構-6.6 二叉樹的性質》

  1. 在二叉樹的第 i 層上至多有2i1個結點。

  2. 深度為 k 的二叉樹上至多含2k1個結點(k≥1)

  3. 對任何一棵二叉樹,若它含有n0個葉子結點、n2個度為 2 的結點,則必存在關係式:n0=n2+1

  4. 具有 n 個結點的完全二叉樹的深度為log2n+1。(x表示不大於x的最大整數)。

  5. n個結點的二叉樹中,完全二叉樹具有最小的路徑長度。

  6. 如果對一棵有n個結點的完全二叉樹(其深度為log2n+1)的結點按層序編號(從第1層到底log2n+1層,每層從做到右),則對任一結點i(1<=i<=n),有: a、 如果i=1,則結點i無雙親,是二叉樹的根;如果i>1,則其雙親的編號是 i/2(整除)。 b、如果2i>n,無左孩子;否則,其左孩子是結點2i。 b、如果2i+1>n,則結點i無右孩子;否則,其右孩子是結點2i+1。

注:根據牛客網筆試題來理解這些概念性的知識知識點

1.2.2、二叉樹的儲存結構

  1. 順序儲存結構:僅僅適用於滿或完全二叉樹,結點之間的層次關係由性質5確定。順序儲存結構將資料儲存在固定的陣列中,然在遍歷速度上有一定的優勢,但因所佔空間比較大,是非主流二叉樹。二叉樹通常以鏈式儲存。

    二叉樹的順序儲存
  2. 二叉連結串列法:每個節點儲存左子樹和右子樹。三叉連結串列:左子樹、右子樹、父節點,總的指標是n+2。

二叉樹的鏈式儲存
  1. 在有n個結點的二叉連結串列中,值為非空的鏈域的個數為n-1。在有N個結點的二叉連結串列中必定有2N個鏈域。除根結點外,其餘N-1個結點都有一個父結點。所以,一共有N-1個非空鏈域,其餘2N-(N-1)=N+1個為空鏈域。

  2. 二叉鏈儲存法也叫孩子兄弟法,左指標指向左孩子,右指標指向右兄弟。而中序遍歷的順序是左孩子,根,右孩子。這種遍歷順序與儲存結構不同,因此需要堆疊儲存中間結果。而中序遍歷檢索二叉樹時,由於其儲存結構跟遍歷順序相符,因此不需要用堆疊。

注:大概瞭解,根據後面的程式來理解這些概念性的知識知識點

1.2.3、遍歷二叉樹和線索二叉樹

見參考文獻4《大話資料結構-6.8 遍歷二叉樹和6.10 線索二叉樹》

遍歷二叉樹:使得每一個結點均被訪問一次,而且僅被訪問一次。非遞迴的遍歷實現要利用棧。

  • 先序遍歷DLR:根結點->左子樹->右子樹

  • 中序遍歷LDR:左子樹->根結點->右子樹。必須要有中序遍歷才能得到一棵二叉樹的正確順序

  • 後續遍歷LRD:左子樹->右子樹->根結點。需要棧的支援。

  • 層次遍歷:用一維陣列儲存二叉樹時,總是以層次遍歷的順序儲存結點。層次遍歷應該藉助佇列。

線索二叉樹:對二叉樹所有結點做某種處理可在遍歷過程中實現;檢索(查詢)二叉樹某個結點,可通過遍歷實現;如果能將二叉樹線索化,就可以簡化遍歷演算法,提高遍歷速度,目的是加快查詢結點的前驅或後繼的速度。

如何線索化?以中序遍歷為例,若能將中序序列中每個結點前趨、後繼資訊儲存起來,以後再遍歷二叉樹時就可以根據所儲存的結點前趨、後繼資訊對二叉樹進行遍歷。對於二叉樹的線索化,實質上就是遍歷一次二叉樹,只是在遍歷的過程中,檢查當前結點左,右指標域是否為空,若為空,將它們改為指向前驅結點或後繼結點的線索。前驅就是在這一點之前走過的點,不是下一將要去往的點。

加上結點前趨後繼資訊(結索)的二叉樹稱為線索二叉樹。n個結點的線索二叉樹上每個結點有2個指標域(指向左孩子和右孩子),總共有2n個指標域;一個n個結點的樹有n-1條邊,那麼空指標域= 2n - (n-1) = n + 1,即線索數為n+1。指標域tag為0,存放孩子指標,為1,存放前驅/後繼節點指標。

線索樹下結點x的前驅與後繼查詢:設結點x相應的左(右)標誌是線索標誌,則lchild(rchild)就是前驅(後繼),否則:

  • LDR–前驅:左子樹中最靠右邊的結點;後繼:右子樹中最靠左邊的結點

  • LRD–前驅:右子樹的根,若無右子樹,為左子樹跟。後繼:x是根,後繼是空;x是雙親的右孩子、x是雙親的左孩子,但雙親無右孩子,雙親是後繼;x是雙親的左孩子,雙親有右孩子,雙親右子樹中最左的葉子是後繼

  • DLR–對稱於LRD線索樹—將LRD中所有左右互換,前驅與後繼互換,得到DLR的方法。

  • 為簡化線索連結串列的遍歷演算法,仿照線性連結串列,為線索連結串列加上一頭結點,約定: a、頭結點的lchild域:存放線索連結串列的根結點指標; b、頭結點的rchild域: 中序序列最後一個結點的指標; c、中序序列第一結點lchild域指向頭結點; d、中序序列最後一個結點的rchild域指向頭結點;

中序遍歷的線索二叉樹以及線索二叉樹連結串列示意圖

一棵左右子樹均不空的二叉樹在前序線索化後,其中空的鏈域的個數是1。前序和後續線索化後空鏈域個數都是1,中序是2。二叉樹線上索化後,仍不能有效求解的問題是前序求前序先驅,後序求後序後繼。

中序遍歷的順序為:左、根、右,所以對於每一非空的線索,左子樹結點的後繼為根結點,右子樹結點的前驅為根結點,再遞迴的執行上面的過程,可得非空線索均指向其祖先結點。在中序線索二叉樹中,每一非空的線索均指向其祖先結點。

在二叉樹上加上結點前趨、後繼線索後,可利用線索對二叉樹進行遍歷,此時,不需棧,也不需遞迴基本步驟

  1. p=T->lchild; p指向線索連結串列的根結點;

  2. 若線索連結串列非空,迴圈: a、迴圈,順著p左孩子指標找到最左下結點;訪問之; b、若p所指結點的右孩子域為線索,p的右孩子結點即為後繼結點迴圈: p=p->rchild; 並訪問p所指結點;(在此迴圈中,順著後繼線索訪問二叉樹中的結點) c、一旦線索“中斷”,p所指結點的右孩子域為右孩子指標,p=p->rchild,使 p指向右孩子結點;

注:大概瞭解,根據《大話資料結構》和後面的程式來理解這些概念性的知識知識點

1.2.4、樹和森林

樹的儲存結構

  1. 雙親表示法
  2. 孩子表示法
  3. 利用圖表示樹
  4. 孩子兄弟表示法(二叉樹表示法):連結串列中每個結點的兩指標域分別指向其第一個孩子結點和下一個兄弟結點

將樹轉化成二叉樹:右子樹一定為空

  1. 加線:在兄弟之間加一連線
  2. 抹線:對每個結點,除了其左孩子外,去除其與其餘孩子之間的關係
  3. 旋轉:以樹的根結點為軸心,將整樹順時針轉45°

森林轉換成二叉樹

  1. 將各棵樹分別轉換成二叉樹
  2. 將每棵樹的根結點用線相連
  3. 以第一棵樹根結點為二叉樹的根

樹與轉換後的二叉樹的關係:轉換後的二叉樹的先序對應樹的先序遍歷;轉換後的二叉樹的中序對應樹的後序遍歷

1.2.5、哈弗曼樹/霍夫曼樹

一些概念

  1. 路徑:從一個祖先結點到子孫結點之間的分支構成這兩個結點間的路徑;

  2. 路徑長度:路徑上的分支數目稱為路徑長度;

  3. 樹的路徑長度:從根到每個結點的路徑長度之和。

  4. 結點的權:根據應用的需要可以給樹的結點賦權值;

  5. 結點的帶權路徑長度:從根到該結點的路徑長度與該結點權的乘積;

  6. 樹的帶權路徑長度=樹中所有葉子結點的帶權路徑之和;通常記作 WPL=∑wi×li

  7. 哈夫曼樹:假設有n個權值(w1, w2, … , wn),構造有n個葉子結點的二叉樹,每個葉子結點有一個 wi作為它的權值。則帶權路徑長度最小的二叉樹稱為哈夫曼樹。最優二叉樹。

字首碼的定義:在一個字符集中,任何一個字元的編碼都不是另一個字元編碼的字首。霍夫曼編碼就是字首碼,可用於快速判斷霍夫曼編碼是否正確。霍夫曼樹是滿二叉樹,若有n個節點,則共有(n+1)/2個碼子

給定n個權值作為n的葉子結點,構造一棵二叉樹,若帶權路徑長度達到最小,稱這樣的二叉樹為最優二叉樹,也稱為霍夫曼樹(Huffman Tree)。霍夫曼樹是帶權路徑長度最短的樹,權值較大的結點離根較近。

假設哈夫曼樹是二叉的話,則度為0的結點個數為N,度為2的結點個數為N-1,則結點總數為2N-1。哈夫曼樹的結點個數必為奇數。

哈夫曼樹不一定是完全二叉樹,但一定是最優二叉樹。

若度為m的哈夫曼樹中,其葉結點個數為n,則非葉結點的個數為[(n-1)/(m-1)]。邊的數目等於度。

注:大概瞭解,根據《大話資料結構》和後面的程式來理解這些概念性的知識知識點

1.3、常見的一些樹的應用場景

  1. xml,html等,那麼編寫這些東西的解析器的時候,不可避免用到樹
  2. 路由協議就是使用了樹的演算法
  3. mysql資料庫索引
  4. 檔案系統的目錄結構
  5. 所以很多經典的AI演算法其實都是樹搜尋,此外機器學習中的decision tree也是樹結構

2、二叉樹的建立

2.1、二叉樹的節點表示

通過使用Node類中定義三個屬性,分別為值data左孩子lchild右孩子rchild

class Node():
    """節點類"""
    def __init__(self,data=None,lchild=None,rchild=None):
        self.data = data  # 表示資料域
        self.lchild = lchild  # 表示左子樹
        self.rchild = rchild  # 表示右子樹

2.2、二叉樹的兩種建立方式

建立樹的實現有兩種,遞迴建樹層次建樹,這兩種分別是基於堆疊佇列來實現的。

2.2.1、遞迴建樹

遞迴建樹的過程無非就是一路走到底,但是需要將節點的左右孩子節點對其餘的節點相關聯起來。因此,我們可以如此來實現: 1. 採用擴充套件二叉樹方法實現前序序列建立唯一二叉樹 2. 採用前序序列中序序列建立唯一二叉樹 3. 採用中序序列後序序列建立唯一二叉樹

2.2.1.1、採用擴充套件二叉樹方法實現前序序列建立唯一二叉樹

如果我們要建立一個如下圖左邊所示的二叉樹,為了能讓每個節點確認是否有左右孩子,我們對它進行擴充套件,變成下圖右邊所示的樣子,也就是將二叉樹中每個節點的空指標引出一個虛節點,其值為一特定的值,比如“#”。我們稱這種處理後的二叉樹為原二叉樹的擴充套件二叉樹,擴充套件二叉樹就可以做到一個遍歷序列唯一確定一顆二叉樹了,下圖右邊的前序遍歷序列abd#g###ce##fh###

注意資料輸入格式為前序遍歷abd#g###ce##fh###

輸出格式為: - 前序遍歷:a b d g c e f h - 中序遍歷:d g b a e c h f - 後序遍歷:g d b e h f c a - 層序遍歷:a b c d e f g h

完整程式碼 pre_order_Tree.py

# -*- coding:UTF-8 -*-

'''
前序遍歷建立二叉樹

思路:
參見

程式設計環境:
Python3.5.2

作者:
CSDN部落格:https://my.csdn.net/yeqiang19910412
Github:https://github.com/YeQiang1075736553

日期:
2018.8.13
'''

class Node():
    """節點類"""
    def __init__(self,data=None,lchild=None,rchild=None):
        self.data = data  # 表示資料域
        self.lchild = lchild  # 表示左子樹
        self.rchild = rchild  # 表示右子樹

class BinaryTree():
    def __init__(self,data_list):
        """初始化傳入的列表為迭代器"""
        self.iter = iter(data_list)

    def pre_order_create(self,root=None):
        """前序遍歷建立二叉樹"""
        try:
            # 獲取下一個元素
            next_data = next(self.iter)
            # 如果next_data為"#",則判斷為到達擴充套件二叉樹的葉子節點
            if next_data == "#":
                root = None
            else:
                root = Node(next_data)
                root.lchild = self.pre_order_create(root.lchild)
                root.rchild = self.pre_order_create(root.rchild)
        except Exception as e:
            print(e)
        return root

    def pre_order_traverse(self,root):
        """遞迴實現前序遍歷"""
        if root == None:
            return
        print(root.data,end=" ")
        self.pre_order_traverse(root.lchild)
        self.pre_order_traverse(root.rchild)

    def in_order_traverse(self,root):
        """遞迴實現後序遍歷"""
        if root == None:
            return
        self.in_order_traverse(root.lchild)
        print(root.data,end=" ")
        self.in_order_traverse(root.rchild)

    def post_order_traverse(self,root):
        """遞迴實現後序遍歷"""
        if root == None:
            return
        self.post_order_traverse(root.lchild)
        self.post_order_traverse(root.rchild)
        print(root.data,end=" ")

    def level_order_traverse(self,root):
        """佇列實現層序遍歷"""
        if root == None:
            return
        queue = []
        queue.append(root)
        while queue:
            node = queue.pop(0)
            print(node.data,end=" ")
            if node.lchild != None:
                queue.append(node.lchild)
            if node.rchild != None:
                queue.append(node.rchild)

if __name__ == "__main__":
    str = "abd#g###ce##fh###"
    data = list(str)
    binary_tree = BinaryTree(data)
    root = binary_tree.pre_order_create()
    print("遞迴實現前序遍歷")
    binary_tree.pre_order_traverse(root) # 遞迴實現前序遍歷
    print("\n")
    print("遞迴實現中序遍歷")
    binary_tree.in_order_traverse(root) # 遞迴實現中序遍歷
    print("\n")
    print("遞迴實現後序遍歷")
    binary_tree.post_order_traverse(root) # 遞迴實現後序遍歷
    print("\n")
    print("佇列實現層序遍歷") # 佇列實現層序遍歷
    binary_tree.level_order_traverse(root)

:首先我們傳入的引數是一個預設的節點,其data資料域為None,然後我們從列表中取一個值,賦值給節點資料域,然後就是遞迴了,將左右孩子節點關聯起來。

執行結果

主要參考資料為參考文獻6、7、8。

2.2.1.2、採用前序序列中序序列建立唯一二叉樹

1、演算法思想

和方式一不同的是,這裡的序列不會給出空節點的表示,所以如果只給出先序序列,中序序列,後序序列中的一種,不能唯一確定一棵二叉樹。但是如果給出先序序列和中序序列就可以唯一確定一棵樹。根據三種遍歷方式的特點,我們可以利用先序序列確定根節點,可以利用中序序列確定左右孩子

根據先序序列的“根節點->左子樹->右子樹”性質可得先序序列的第一個一定為這棵樹的根節點,而又根據中序序列左子樹->根節點->右子樹”的性質,由先序序列得到的根節點可以將中序序列分為左子樹中序序列和右子樹中序序列。然後根據序列的個數相同,可以將先序序列分為左子樹先序序列和右子樹先序序列。依次遞迴的進行,直到序列長度為空遞迴返回。

例如前序序列a b d g c e f h中序序列d g b a e c h f ,可以構造出唯一的二叉樹,如下圖所示:

2、演算法步驟 : step 1: 先序序列的第一個一定是這棵樹的根節點 step 2:先序序列得到的根節點將中序序列分為左子樹中序序列和右子樹中序序列 step 3:根據序列的個數相同,可以將先序序列分為左子樹先序序列和右子樹先序序列 step 4:依次遞迴的進行,直到序列長度為空

總體就是根據後序來確定根節點,然後通過根節點,在中序序列中確定左子樹和右子樹,在通過左子樹後序序列和右子樹後序序列判斷子樹根節點。

演算法步驟

可以驗證,該二叉樹的後序序列為g d b e h f c a

完整程式碼 pre_in_Tree.py

# -*- coding:UTF-8 -*-

'''
前序序列和中序序列構造二叉樹

思路:
參見

程式設計環境:
Python3.5.2

作者:
CSDN部落格:https://my.csdn.net/yeqiang19910412
Github:https://github.com/YeQiang1075736553

日期:
2018.8.13
'''

class Node():
    """節點類"""
    def __init__(self,data=None,lchild=None,rchild=None):
        self.data = data  # 表示資料域
        self.lchild = lchild  # 表示左子樹
        self.rchild = rchild  # 表示右子樹

class BinaryTree():
    def __init__(self):
        pass

    def pre_in_construct_tree(self,pre_order, mid_order):
        """根據前序序列和中序序列構造二叉樹"""
        if len(pre_order)==0 or len(mid_order)==0:
            return None
        # 前序遍歷的第一個結點一定是根結點
        root_data = pre_order[0]
        root = Node(root_data)
        # for i in range(0,len(mid_order)):
        #     if root_data == mid_order[i]:
        #         break
        i = mid_order.index(root_data) #上面for迴圈替代
        # 遞迴構造左子樹和右子樹
        root.lchild = self.pre_in_construct_tree(pre_order[1:1+i],mid_order[:i])
        root.rchild = self.pre_in_construct_tree(pre_order[1+i:],mid_order[i+1:])
        return root

    def pre_order_traverse(self,root):
        """遞迴實現前序遍歷"""
        if root == None:
            return
        print(root.data,end=" ")
        self.pre_order_traverse(root.lchild)
        self.pre_order_traverse(root.rchild)

    def in_order_traverse(self,root):
        """遞迴實現後序遍歷"""
        if root == None:
            return
        self.in_order_traverse(root.lchild)
        print(root.data,end=" ")
        self.in_order_traverse(root.rchild)

    def post_order_traverse(self,root):
        """遞迴實現後序遍歷"""
        if root == None:
            return
        self.post_order_traverse(root.lchild)
        self.post_order_traverse(root.rchild)
        print(root.data,end=" ")

    def level_order_traverse(self, root):
        """佇列實現層序遍歷"""
        if root == None:
            return
        queue = []
        queue.append(root)
        while queue:
            node = queue.pop(0)
            print(node.data, end=" ")
            if node.lchild != None:
                queue.append(node.lchild)
            if node.rchild != None:
                queue.append(node.rchild)

if __name__ == '__main__':
    str_pre = "abdgcefh"
    str_mid = "dgbaechf"
    pre_order = list(str_pre)
    mid_order = list(str_mid)
    binary_tree = BinaryTree()
    root = binary_tree.pre_in_construct_tree(pre_order, mid_order)
    print("遞迴實現前序遍歷")
    binary_tree.pre_order_traverse(root) # 遞迴實現前序遍歷
    print("\n")
    print("遞迴實現中序遍歷")
    binary_tree.in_order_traverse(root) # 遞迴實現中序遍歷
    print("\n")
    print("遞迴實現後序遍歷")
    binary_tree.post_order_traverse(root) # 遞迴實現後序遍歷
    print("\n")
    print("佇列實現層序遍歷") # 佇列實現層序遍歷
    binary_tree.level_order_traverse(root)

主要參考資料為參考文獻9、10、11、13。

2.2.1.3、採用中序序列後序序列建立唯一二叉樹

1、演算法思想

和方式二類似,這裡的序列不會給出空節點的表示,所以如果只給出先序序列,中序序列,後序序列中的一種,不能唯一確定一棵二叉樹。但是如果給出中序序列和後序序列就可以唯一確定一棵樹。根據三種遍歷方式的特點,我們可以利用後序序列確定根節點,可以利用中序序列確定左右孩子

後序序列左子樹->右子樹->根節點”性質可知後序序列的最後一個一定為這棵樹的根節點,而又根據中序序列左子樹->根節點->右子樹”的性質,由後序序列得到的根節點可以將中序序列分為左子樹中序序列和右子樹中序序列。然後根據序列的個數相同,可以將後序序列分為左子樹後序序列和右子樹後序序列。依次遞迴的進行,直到序列長度為空遞迴返回。

例如中序序列d g b a e c h f 後序序列g d b e h f c a ,可以構造出唯一的二叉樹,如下圖所示:

2、演算法步驟 : step 1: 後序序列的最後一個一定是這棵樹的根節點 step 2:後序序列得到的根節點將中序序列分為左子樹中序序列和右子樹中序序列 step 3:根據序列的個數相同,可以將後序序列分為左子樹後序序列和右子樹後序序列 step 4:依次遞迴的進行,直到序列長度為空

總體就是根據後序來確定根節點,然後通過根節點,在中序序列中確定左子樹和右子樹,在通過左子樹後序序列和右子樹後序序列判斷子樹根節點。

演算法步驟

可以驗證,該二叉樹的前序序列為a b d g c e f h

完整程式碼 in_post_Tree.py

# -*- coding:UTF-8 -*-

'''
中序序列和後序序列構造二叉樹

思路:
參見

程式設計環境:
Python3.5.2

作者:
CSDN部落格:https://my.csdn.net/yeqiang19910412
Github:https://github.com/YeQiang1075736553

日期:
2018.8.13
'''

class Node():
    """節點類"""
    def __init__(self,data=None,lchild=None,rchild=None):
        self.data = data  # 表示資料域
        self.lchild = lchild  # 表示左子樹
        self.rchild = rchild  # 表示右子樹

class BinaryTree():
    def __init__(self):
        pass

    def in_post_construct_tree(self,mid_order, post_order):
        """根據中序序列和後序序列構造二叉樹"""
        if len(mid_order)==0 or len(post_order)==0:
            return None
        # 後序遍歷的最後一個結點一定是根結點
        root_data = post_order[-1]
        root = Node(root_data)
        # for i in range(0,len(mid_order)):
        #     if root_data == mid_order[i]:
        #         break
        i = mid_order.index(root_data) #上面for迴圈替代
        # 遞迴構造左子樹和右子樹
        root.lchild = self.in_post_construct_tree(mid_order[:i],post_order[:i])
        root.rchild = self.in_post_construct_tree(mid_order[i+1:],post_order[i:-1])
        return root

    def pre_order_traverse(self,root):
        """遞迴實現前序遍歷"""
        if root == None:
            return
        print(root.data,end=" ")
        self.pre_order_traverse(root.lchild)
        self.pre_order_traverse(root.rchild)

    def in_order_traverse(self,root):
        """遞迴實現後序遍歷"""
        if root == None:
            return
        self.in_order_traverse(root.lchild)
        print(root.data,end=" ")
        self.in_order_traverse(root.rchild)

    def post_order_traverse(self,root):
        """遞迴實現後序遍歷"""
        if root == None:
            return
        self.post_order_traverse(root.lchild)
        self.post_order_traverse(root.rchild)
        print(root.data,end=" ")

    def level_order_traverse(self, root):
        """佇列實現層序遍歷"""
        if root == None:
            return
        queue = []
        queue.append(root)
        while queue:
            node = queue.pop(0)
            print(node.data, end=" ")
            if node.lchild != None:
                queue.append(node.lchild)
            if node.rchild != None:
                queue.append(node.rchild)

if __name__ == '__main__':
    str_mid = "dgbaechf"
    str_post = "gdbehfca"

    mid_order = list(str_mid)
    post_order = list(str_post)
    binary_tree = BinaryTree()
    root = binary_tree.in_post_construct_tree(mid_order, post_order)
    print("遞迴實現前序遍歷")
    binary_tree.pre_order_traverse(root) # 遞迴實現前序遍歷
    print("\n")
    print("遞迴實現中序遍歷")
    binary_tree.in_order_traverse(root) # 遞迴實現中序遍歷
    print("\n")
    print("遞迴實現後序遍歷")
    binary_tree.post_order_traverse(root) # 遞迴實現後序遍歷
    print("\n")
    print("佇列實現層序遍歷") # 佇列實現層序遍歷
    binary_tree.level_order_traverse(root)

主要參考資料為參考文獻9、12、13。

2.2.2、層次建樹

層次建樹就是基於佇列的操作,利用佇列先進先出的特點,每次我們訪問一個節點的時候,將其存入佇列中,待遍歷完當前節點的左右孩子節點,佇列就彈出一個節點,之後的操作都是一樣的。

  1. 採用層序序列建立二叉樹
  2. 採用中序序列層序序列建立唯一二叉樹

2.2.2.1、採用層序序列建立二叉樹

如參考文獻6中所示,但是隻能建立完全二叉樹。能否建立任意的二叉樹,這個還需要進一步討論。

2.2.2.2、採用中序序列和層序序列建立唯一二叉樹

1、演算法步驟 : step 1: 初始:用層序遍歷確定頂節點,在中序遍歷中,利用頂節點劃分出左右子樹。 step 2: 迭代:對各個子樹分別執行三步操作,1.在層序序列中,找出子樹節點集合中,最靠前的節點,這個節點即為子樹的頂節點;2.在中序序列中找 1 中得到的頂節點,並劃分開頂節點的左右子樹;

跟(中序+前序)和(中序+後序)不同之處在於沒有迭代的第 3 步,層序是無法直接劃分得到左右子樹的節點集合的。但這並不妨礙正常的處理。層序是用來找到子樹的頂節點的,而頂節點即是所有子樹的節點中,在層序遍歷中最靠前的節點。

演算法步驟

完整程式碼 in_level_Tree.py

# -*- coding:UTF-8 -*-

'''
中序序列和層序序列構造二叉樹

思路:
參見

程式設計環境:
Python3.5.2

作者:
CSDN部落格:https://my.csdn.net/yeqiang19910412
Github:https://github.com/YeQiang1075736553

日期:
2018.8.13
'''

class Node():
    """節點類"""
    def __init__(self,data=None,lchild=None,rchild=None):
        self.data = data  # 表示資料域
        self.lchild = lchild  # 表示左子樹
        self.rchild = rchild  # 表示右子樹

class BinaryTree():
    def __init__(self):
        pass

    def in_level_construct_tree(self,mid_order,level_order):
        """根據中序序列和層序序列構造二叉樹"""
        if len(mid_order)==0 or len(level_order)==0:
            return None
        # 層序遍歷的第一個結點一定是根結點
        root_data = level_order[
            
           

相關推薦

4

1、樹和二叉樹的一些基本知識點 1.1、樹 一種非線性結構。樹是遞迴結構,在樹的定義中又用到了樹的概念。 1.1.1、樹的基本術語: 樹結點:包含一個數據元素及若干指向子樹的分支; 孩子結點:結點的子樹的根稱為該結點的孩子; 雙親結

實驗 4的實驗 1

一、實驗目的 1、 熟練理解樹和二叉樹的相關概念,掌握的儲存結構和相關操作實現; 2、 掌握樹的順序結構的實現; 3、 學會運用樹的知識解決實際問題。 二、實驗內容  自己確定一個二叉樹(樹結點型別、數目和結構自定)利用順序結構方法儲存。 實現樹的構造,並完成:

森林的轉換

樹轉換為二叉樹 (1)加線。在所有兄弟結點之間加一條連線。 (2)去線。樹中的每個結點,只保留它與第一個孩子結點的連線,刪除它與其它孩子結點之間的連線。 (3)層次調整。以樹的根節點為軸心,將整棵樹順時針旋轉一定角度,使之結構層次分明。(注意第一個孩子是結點的左孩子

reat 完成 child names space -1 include ges 中序 編程語言:c++ 截圖展示: 代碼如下: main.cpp 1 #include <iostream> 2 #include <cstdio&g

第六章

a20 cfb 樹和二叉樹 fff itblog ffd ace cab dac 第六章 樹和二叉樹

->最優

nco 代碼實現 type except close 輸出結點 eof fde 左右 文字描述 結點的路徑長度   從樹中一個結點到另一個結點之間的分支構成這兩個結點之間的路徑,路徑上的分支數目稱作路徑長度。 樹的路徑長度    從樹根到每一個結點的路徑長度之和叫樹的路徑長

數據結構——第三章:01的類型定義

有序 存在 lin 深度 操作 root 判定樹 delet eem 1.樹的類型定義: (1)數據對象D:D是具有相同特性的數據元素的集合。 (2)數據關系R:若D為空集,則成為空樹 否則:在D中存在唯一的稱為根的數據元素root。當n>1時,其余結點可分為n(n&

資料結構——第三章:02

1.二叉樹的儲存結構: (1)二叉樹的順序儲存表示: #define MAX_TREE_SIZE 100 //二叉樹的最大結點數 typedef TElemType SqBiTree[MAX_TREE_SIZE];  SqBiTree bt; (2)二叉樹的鏈式儲存表示: ①二叉連結

資料結構——第三章:03森林

1.樹的三種儲存結構: (1)雙親表示法: #define MAX_TREE_SIZE 100 結點結構: typedef struct PTNode {   Elem data;   int parent; //雙親位置域 } PTNode; (2)孩子雙親連結串列表示法: &nbs

第六章--Huffman-計算機17級

解析在下面,有什麼問題歡迎各位大佬指正   p1-1: 這個主要得看懂題,其實就是在考你哈夫曼樹的構造:每次把權值最小的兩顆二叉樹合併 ,越往下肯定權值越小,所以這句話肯定是對的 x2-1: d肯定不一定啊 x2-2: x2-3:

第六章--森林-計算機17級 7-2 家譜處理 (30 分)

7-2 家譜處理 (30 分) 人類學研究對於家族很感興趣,於是研究人員蒐集了一些家族的家譜進行研究。實驗中,使用計算機處理家譜。為了實現這個目的,研究人員將家譜轉換為文字檔案。下面為家譜文字檔案的例項: John Robert Frank And

第六章--森林-計算機17級

解析在後面,有問題的話歡迎各位大佬指正:   答案解析: 提示:不會做就畫圖,原理雖然不理解但答案基本都能出來 p1-1: x2-1: x2-2:同b1-1 x2-3:這個其實你只要會了森林轉換成二叉樹的方法畫個圖自

第六章--森林-計算機17級 7-1 的同構 (25 分)(答案超詳解)

7-1 樹的同構 (25 分) 給定兩棵樹T1和T2。如果T1可以通過若干次左右孩子互換就變成T2,則我們稱兩棵樹是“同構”的。例如圖1給出的兩棵樹就是同構的,因為我們把其中一棵樹的結點A、B、G的左右孩子互換後,就得到另外一棵樹。而圖2就不是同構的。  

第六章作業1—--計算機17級 6-2 的遍歷 (25 分)

6-2 二叉樹的遍歷 (25 分) 本題要求給定二叉樹的4種遍歷。 函式介面定義: void InorderTraversal( BinTree BT ); void PreorderTraversal( BinTree BT ); void PostorderT

第六章作業1—--計算機17級 7-1 根據後序中序遍歷輸出先序遍歷 (25 分)

7-1 根據後序和中序遍歷輸出先序遍歷 (25 分) 本題要求根據給定的一棵二叉樹的後序遍歷和中序遍歷結果,輸出該樹的先序遍歷結果。 輸入格式: 第一行給出正整數N(≤30),是樹中結點的個數。隨後兩行,每行給出N個整數,分別對應後序遍歷和中序遍歷結果,數字間以空

第六章作業1—--計算機17級 6-3 先序輸出葉結點 (15 分)

6-3 先序輸出葉結點 (15 分) 本題要求按照先序遍歷的順序輸出給定二叉樹的葉結點。 函式介面定義: void PreorderPrintLeaves( BinTree BT ); 其中BinTree結構定義如下: typedef struct TN

第六章作業1—--計算機17級 6-1 求高度 (20 分)

6-1 求二叉樹高度 (20 分) 本題要求給定二叉樹的高度。 函式介面定義: int GetHeight( BinTree BT ); 其中BinTree結構定義如下: typedef struct TNode *Position; typedef P

第六章作業1—--計算機17級

 解析在下面      p1-1: 前序根,左,右。 中序左,根,右。 後序左,右,根。 中和後一樣,肯定是都沒有右孩子。 p1-3: p1-4: p1-5:

實驗五:的實驗2

使用二叉樹的鏈式儲存結構,建立一棵二叉樹,進行前序、中序以及後序遍歷,同時求得二叉樹的結點個數以及葉子結點個數。 程式原始碼: # ifndef Bitree_H # define Bitree_H struct BiNode { char data; BiNode *lchi

實驗四:的實驗一

實驗內容: 自己確定一個二叉樹(樹結點型別、數目和結構自定)利用順序結構方法儲存,實現樹的構造。 原始碼: #include <stdio.h> # include<string.h> # include<math.h> #include<s