二叉平衡樹 python 列表 遞迴

XIAKE_XING發表於2021-01-03

二叉平衡樹-python-列表-遞迴

用列表遞迴的方式表示二叉樹,例如:只有根root的樹用列表來表示就是 [None, None], 再例如一個這樣的樹:

在這裡插入圖片描述

用列表來表示就是:
[[None, None, 222], [None, [None, None, 444], 333], 111]
可以簡單的認為看見一個 ‘ [ ] ’ 括號就認為它是一個節點,裡面的元素有該節點的左kid,右kid,以及這個節點的data.

所以,我構建了一個這樣的樹結構:

class Node(object):
    def __init__(self, data=None, l_kid=None, r_kid=None):
        self.data = data
        self.left = l_kid
        self.right = r_kid


class Tree(object):
    list_with_node = None   # 例 :只有根的二叉樹為 [None, None, root]
    list_only = None        # 例 :只有根的二叉樹為 [None, None]
    list_with_data = None   # 例 :只有根的二叉樹為 [None, None, root.data]

    def __init__(self, root=None):
        self.root = root

    # 例:[None, None, root]表示一個只有root節點的樹,有三個元素:左子樹,右子樹,該節點物件
    def list_with_node_travel(self, node):
        if not node:
            return None
        total = [
            self.list_with_node_travel(node.left),
            self.list_with_node_travel(node.right),
            node,
        ]
        return total

    # 例:[None, None]表示一個只有root節點的樹, [[None, None], [None, None]]表示一個第三層全為None的平衡樹
    def list_only_travel(self, node):
        if not node:
            return None
        total = [
            self.list_only_travel(node.left),
            self.list_only_travel(node.right),
            # node,
         ]
        return total

    # 例:[None, None, root.data]表示一個只有root節點的樹,有三個元素:左子樹,右子樹,該節點的data
    def list_with_data_travel(self, node):
        if not node:
            return None
        total = [
            self.list_with_data_travel(node.left),
            self.list_with_data_travel(node.right),
            node.data,
         ]
        return total

    def update_list(self):
        Tree.list_with_node = self.list_with_node_travel(self.root)
        Tree.list_only = self.list_only_travel(self.root)
        Tree.list_with_data = self.list_with_data_travel(self.root)

    # 層次遍歷list,也可以說是樹,當遇到第一個None時,返回None在該層的index,以及該層的深度depth
    # depth從2開始,預設root存在, 換句話說Tree.list_only預設為list型別.
    def _append(self, x, depth=2):
        if isinstance(x, list):
            if None in x:
                # 返回該層第一次出現None的index和該層的depth
                return x.index(None), depth
            # 如果該層沒有None,說明該層滿了,那就用迴圈開啟x內的所有list,並把他們拼在一起
            # 這步的意思就是,進入下一層,遞迴判斷第一次出現None的座標
            a = []
            for i in x:
                a.extend(i)
            depth += 1
            y = depth
            return self._append(a, depth=y)

    # 傳入一個node,新增在樹的結構裡
    def append(self, node):
        index, depth = self._append(Tree.list_only)
        code = self._arithmetic(index, depth)
        if len(code) == 1:
            # [None, None, node]為這個新node,左右子樹為None, 節點物件為node.
            Tree.list_with_node[int(code)] = [None, None, node]
            # 為該node物件建立父子關係
            if int(code) == 0:
                self.root.left = node
            else:
                self.root.right = node
            return self.update_list()
        current_list = Tree.list_with_node
        last = int(code[-1])
        # 先走到這個新節點的父輩節點.
        code_to_father = code[:-1]
        for i in code_to_father:
            current_list = current_list[int(i)]
        # 此時current_list來到父節點[x, x, 父node],這時再看最後一步的方向來設定新node為父節點的哪邊。
        if last == 0:
            current_list[-1].left = node
        else:
            current_list[-1].right = node
        current_list[last] = [None, None, node]
        return self.update_list()

    # 根據depth和該層第一次出現None的index,得到一個神祕碼(反映出該None在數中的座標)
    def _arithmetic(self, index, depth):
        """
            這個演算法建議畫圖琢磨
            (position, depth),例如(6, 4)為第4層從左到右第6個元素。該層滿元素個數為2 ** (depth - 1) = 8個
            步數 = depth - 1 = 3 步
            1. 因為6 > (8 / 2),所以從root開始找該元素時第一步是走root.right
            2. 再用6 - (8 / 2) = 2, 因為 2 <= ((8 / 2) / 2),所以這步是走左邊left,root.right.left
            3. 上一步得出左邊後,2就不用減((8 / 2) / 2), 因為2 > (((8 / 2) / 2) / 2),所以走右邊right
            4. 最後路線為:root.right.left.right總共走三步就到達該node準確位置
            5. 神祕碼就為 '101' , '1'為right, '0'為left.  遍歷找node或node.data時會用到神祕碼
        """
        position = index + 1
        # times為步數
        times = depth - 1
        # nums為該層滿元素的個數(depth為2時,該層最多2個node,第三層最多4個node)
        nums = 2 ** (depth - 1)
        i = 1
        code = ''
        while i <= times:
            if position <= nums / 2:
                magic = '0'
                nums = nums / 2
            else:
                magic = '1'
                position = position - (nums / 2)
                nums = nums / 2
            code += magic
            i += 1
        return code



在這裡插入圖片描述
我們先構建這樣的樹結構,看看輸出:

if __name__ == '__main__':
    node_a = Node(data=111)
    node_b = Node(data=222)
    node_c = Node(data=333)
    node_d = Node(data=444)
    node_e = Node(data=555)
    node_f = Node(data=666)

    # 設定樹關係
    tree = Tree(node_a)
    node_a.left = node_b
    node_b.left = node_d
    node_a.right = node_c
    node_c.left = node_e
    node_e.right = node_f
    
    tree.update_list()
    print(tree.__class__.list_with_data)
    # 輸出為: [[[None, None, 444], None, 222], [[None, [None, None, 666], 555], None, 333], 111]

此輸出list描述的圖,畫出來後就是上述的圖。

可以這樣去標記列表:
在這裡插入圖片描述

下面來增加幾個節點來看看輸出:
在這裡插入圖片描述

# append節點
    add_1 = Node(data='add_1')
    add_2 = Node(data='add_2')
    add_3 = Node(data='add_3')
    add_4 = Node(data='add_4')
    add_5 = Node(data='add_5')
    add_6 = Node(data='add_6')
    add_7 = Node(data='add_7')
    add_8 = Node(data='add_8')
    add_9 = Node(data='add_9')

    tree.append(add_1)
    tree.append(add_2)
    tree.append(add_3)
    tree.append(add_4)
    tree.append(add_5)
    tree.append(add_6)
    tree.append(add_7)
    tree.append(add_8)
    tree.append(add_9)
    print(tree.__class__.list_with_data)
# 輸出為:
[[[[None, None, 'add_3'], [None, None, 'add_4'], 444], [[None, None, 'add_5'], [None, None, 'add_6'], 'add_1'], 222], [[[None, None, 'add_7'], [None, None, 666], 555], [[None, None, 'add_8'], [None, None, 'add_9'], 'add_2'], 333], 111]

此結果所描繪的樹結構就是上圖所示的樹結構。

相關文章