1. 程式人生 > 其它 >學習筆記——二叉樹(python實現)

學習筆記——二叉樹(python實現)

技術標籤:python資料結構二叉樹

樹形結構在資料結構中也是非常重要的一種存在,其特點為非線性,各個節點之間以分支關係相聯絡。
樹(Tree)是個結點的有限集。在任意一棵樹中:
(1)有且僅有一個特定的稱為根(Root)的節點;
(2)當n>1時,其餘節點可分為m(m>0)個互不相交的有限集T_1,T_2,…,T_m,其中每一個集合本身又是一棵樹,並且稱為根的子樹(SubTree)。
有關術語及定義:
(1)結點:包含一個數據元素及若干指向其子樹的分支。
(2)度:節點所擁有的子樹數量。
(3)葉子:度為零的結點。
(4)分支節點:度不為零的結點。
(5)樹的度:度的最大的值。

(6)深度(高度):樹的最大層次
二叉樹的性質:
性質1)在二叉樹的第i層上至多有2^{i-1}個結點(i\geq 1)。
性質2)深度為k的二叉樹至多有2^{k}-1個結點(k\geq 1)。
性質3)對任何一棵二叉樹,如果其葉子節點數為n_{0},度為2的結點數為n_2,則n_0=n_2+1。

在這裡插入圖片描述 下面介紹完全二叉樹的兩個特性:

性質4)具有n個結點的完全二叉樹的深度為[log_{2}n]+1,其中[x]表示不大於x的最大整數。
性質5)如果對一棵有n個結點的完全二叉樹的結點按層序編號(從第一層到最後一層,每層從左到右),則對任一結點i(1\leq i\leq n),有:
(1)如果i=1,則結點i是二叉樹的根,無雙親;如果i>1,則其雙親結點為[1/2]。

(2)如果2i>n,則結點i無左孩子;否則其左孩子是結點2i。
(3)如果2i+1>n,則結點i無右孩子;否則其右孩子是結點2i+1。

介紹完了二叉樹的定義及基本性質,接下來,我們需要了解二叉樹的遍歷。所謂二叉樹的遍歷,指的是如何按某種搜尋路徑巡防樹中的每個結點,使得每個結點均被訪問一次,而且僅被訪問一次。對於二叉樹,常見的遍歷方法有:先序遍歷,中序遍歷,後序遍歷,層序遍歷。這些遍歷方法一般使用遞迴演算法實現。
  先序遍歷的操作定義為:若二叉樹為空,為空操作;否則(1)訪問根節點;(2)先序遍歷左子樹;(3)先序遍歷右子樹。
  中序遍歷的操作定義為:若二叉樹為空,為空操作;否則(1)中序遍歷左子樹;(2)訪問根結點;(3)中序遍歷右子樹。

  後序遍歷的操作定義為:若二叉樹為空,為空操作;否則(1)後序遍歷左子樹;(2)後序遍歷右子樹;(3)訪問根結點。
  層序遍歷的操作定義為:若二叉樹為空,則None;否則從上到下、從左到右按層次進行訪問。

基礎原理就說到這,接下來看一下在電腦中如何實現呢?
(1)建立:個人認為在python中初始化資料結構都十分簡單,更是大同小異,通過上述分析可以知道對於一個樹來說,它需要定義資料節點,左子樹和右子樹。
(2)先序遍歷:首先看其資料域是否為空,若不是空則輸出其值,接下來依次判斷其左子樹,右子樹是否為空,若不是空則再次呼叫此函式(遞迴實現)。
(3)中序遍歷:思路和先序遍歷的實現是一樣的。不過順序有所改變:首先看其左子樹是否為空,若不是空則向下執行,判斷其資料域是否為空,不為空則輸出其資料,最後判斷右子樹是否為空,若不是空則再次呼叫此函式(遞迴實現)。
(4)後序遍歷:和上述過程一樣,不過將其變成了左右根。
(5)樹的高度:可以分為五種情況:
(1).data為空:則直接返回0(空樹)
(2)左子樹,右子樹為空:返回1
(3)左子樹為空,右子樹不為空:返回1+右子樹高度(將右子樹結點遞迴呼叫height()方法
(4)右子樹為空,左子樹不為空:返回1+左子樹高度(將左子樹結點遞迴呼叫height()方法
(5)左右子樹都不為空:則為1+max(左子樹高度,右子樹高度)
(6)層次遍歷:首先建立列表,如果data域不為空就將其加到列表中然後判斷樹高。當其大於等於1時,運用for迴圈將節點返回(注意不是直接將資料返回,所以對每個層還要在建立一個列表,用來返回該層的節點),具體返回過程如下:依次判斷該層的上一個節點的左孩子和右孩子(一定是上一層的左孩子和右孩子這樣才能保證所返回的為該層的值)是否為空,不為空則返回。最後如此列表不為空,將其新增到最初定義的列表當中。當返回層次列表時運用雙層迴圈巢狀,外層迴圈條件為0~height即層數,內層為上述過程中所求得的列表長度,得到其資料值(此返回操作,是因為上述返回的是節點而不是資料,此過程把資料返回確保遍歷的實現)
(7)葉子結點:依舊可以分為五種情況
(1).data為空:則直接返回None(空樹)
(2)左子樹,右子樹為空:返回data
(3)左子樹為空,右子樹不為空:直接對右子樹結點遞迴呼叫leaves()方法
(4)右子樹為空,左子樹不為空:直接對左子樹結點遞迴呼叫leaves()方法
(5)左右子樹都不為空:分別對左,右子樹結點遞迴呼叫leaves()方法
程式碼段:

import uuid
from random import sample

# 二叉樹類
class BTree(object):

    # 初始化
    def __init__(self, data=None, left=None, right=None):
        self.data = data    # 資料域
        self.left = left    # 左子樹
        self.right = right  # 右子樹
      

    # 前序遍歷
    def preorder(self):

        if self.data is not None:
            print(self.data, end=' ')
        if self.left is not None:
            self.left.preorder()
        if self.right is not None:
            self.right.preorder()

    # 中序遍歷
    def inorder(self):

        if self.left is not None:
            self.left.inorder()
        if self.data is not None:
            print(self.data, end=' ')
        if self.right is not None:
            self.right.inorder()

    # 後序遍歷
    def postorder(self):

        if self.left is not None:
            self.left.postorder()
        if self.right is not None:
            self.right.postorder()
        if self.data is not None:
            print(self.data, end=' ')

    # 層序遍歷
    def levelorder(self):



        # 層序遍歷列表
        level_order = []
        # 是否新增根節點中的資料
        if self.data is not None:
            level_order.append([self])

        # 二叉樹的高度
        height = self.height()
                # 返回某個節點的左孩子
        def LChild_Of_Node(node):
            return node.left if node.left is not None else None
        # 返回某個節點的右孩子
        def RChild_Of_Node(node):
            return node.right if node.right is not None else None
        if height >= 1:
            # 對第二層及其以後的層數進行操作, 在level_order中新增節點而不是資料
            for _ in range(2, height + 1):
                level = []  # 該層的節點
                for node in level_order[-1]:
                    # 如果左孩子非空,則新增左孩子
                    if LChild_Of_Node(node):
                        level.append(LChild_Of_Node(node))
                    # 如果右孩子非空,則新增右孩子
                    if RChild_Of_Node(node):
                        level.append(RChild_Of_Node(node))
                # 如果該層非空,則新增該層
                if level:
                    level_order.append(level)

            # 取出每層中的資料
            for i in range(0, height):  # 層數
                for index in range(len(level_order[i])):
                    level_order[i][index] = level_order[i][index].data

        return level_order

    # 二叉樹的高度
    def height(self):
        # 空的樹高度為0, 只有root節點的樹高度為1
        if self.data is None:
            return 0
        elif self.left is None and self.right is None:
            return 1
        elif self.left is None and self.right is not None:
            return 1 + self.right.height()
        elif self.left is not None and self.right is None:
            return 1 + self.left.height()
        else:
            return 1 + max(self.left.height(), self.right.height())

    # 二叉樹的葉子節點
    def leaves(self):

        if self.data is None:
            return None
        elif self.left is None and self.right is None:
            print(self.data, end=' ')
        elif self.left is None and self.right is not None:
            self.right.leaves()
        elif self.right is None and self.left is not None:
            self.left.leaves()
        else:
            self.left.leaves()
            self.right.leaves()


#外部檔案保證實現
from Binary_Tree import BTree

# 構造二叉樹, BOTTOM-UP METHOD
right_tree = BTree(6)
right_tree.left = BTree(2)
right_tree.right = BTree(4)

left_tree = BTree(5)
left_tree.left = BTree(1)
left_tree.right = BTree(3)

tree = BTree(11)
tree.left = left_tree
tree.right = right_tree

left_tree = BTree(7)
left_tree.left = BTree(3)
left_tree.right = BTree(4)

right_tree = tree # 增加新的變數
tree = BTree(18)
tree.left = left_tree
tree.right = right_tree

print('先序遍歷為:')
tree.preorder() 
print()

print('中序遍歷為:')
tree.inorder()
print()

print('後序遍歷為:')
tree.postorder()
print()

print('層序遍歷為:')
level_order = tree.levelorder()
print(level_order)
print()

height = tree.height()
print('樹的高度為%s.' % height)

print('葉子節點為:')
tree.leaves()
print()


注:該文章參考於https://www.jianshu.com/p/9503238394df