2024年11月7日 星期四

一維串列表示二元搜尋樹

 使用一維串列來表示二元搜尋樹時,我們可以將樹的結構視為完全二元樹,並使用類似於「堆積」的方式進行存儲。這種表示方法將樹的節點按層序排成一維串列,節點之間的父子關係可以由索引計算得出。

一維串列的二元搜尋樹表示方式

在一維串列中:

  • 根節點位於索引 0
  • 對於位於索引 i 的節點:
    • 左子節點位於索引 2*i + 1
    • 右子節點位於索引 2*i + 2
    • 父節點位於索引 (i - 1) // 2

此方法的限制是,二元搜尋樹在某些情況下會變得非常稀疏(例如只有右子樹),導致需要大量空間來存儲空節點(通常設為 None),因此這種方法適合用於完全二元樹或樹較為平衡的情況。


(1) 插入 (Insert)

在二元搜尋樹中插入新值時,需要找到適合的位置。以下是使用一維串列模擬插入的過程:

插入函式

def insert(tree, value):
    if not tree:  # 若樹為空,則將值加入根節點
        tree.append(value)
        return tree

    i = 0  # 從根節點開始
    while i < len(tree):
        if value < tree[i]:  # 若值小於當前節點,則應插入左子樹
            left_child = 2 * i + 1
            if left_child >= len(tree):
                tree.extend([None] * (left_child - len(tree) + 1))
            if tree[left_child] is None:
                tree[left_child] = value
                return tree
            else:
                i = left_child
        else:  # 若值大於等於當前節點,則應插入右子樹
            right_child = 2 * i + 2
            if right_child >= len(tree):
                tree.extend([None] * (right_child - len(tree) + 1))
            if tree[right_child] is None:
                tree[right_child] = value
                return tree
            else:
                i = right_child

測試插入

# 建立並插入節點
bst = []
for value in [5, 3, 2, 7, 1, 6, 4]:
    bst = insert(bst, value)

print("插入後的二元搜尋樹:", bst)

搜尋函式檢查一維串列中的特定數值是否存在,根據二元搜尋樹的規則沿著層序搜尋。

搜尋函式

def search(tree, value):
    i = 0  # 從根節點開始
    while i < len(tree) and tree[i] is not None:
        if tree[i] == value:
            return True
        elif value < tree[i]:  # 若值小於當前節點,搜尋左子樹
            i = 2 * i + 1
        else:  # 若值大於當前節點,搜尋右子樹
            i = 2 * i + 2
    return False

測試搜尋

print("搜尋 5:", search(bst, 5))  # 預期 True
print("搜尋 2:", search(bst, 2))  # 預期 True
print("搜尋 8:", search(bst, 8))  # 預期 False

(3) 遍歷 (Traversal)

以下是前序、中序和後序遍歷函式,對應二元搜尋樹的遍歷順序。

前序遍歷 (Preorder Traversal)

def preorder_traversal(tree, i=0):
    if i >= len(tree) or tree[i] is None:
        return []
    return [tree[i]] + preorder_traversal(tree, 2 * i + 1) + preorder_traversal(tree, 2 * i + 2)

print("前序遍歷:", preorder_traversal(bst))  # 預期 [5, 3, 2, 1, 4, 7, 6]

中序遍歷 (Inorder Traversal)

def inorder_traversal(tree, i=0):
    if i >= len(tree) or tree[i] is None:
        return []
    return inorder_traversal(tree, 2 * i + 1) + [tree[i]] + inorder_traversal(tree, 2 * i + 2)

print("中序遍歷:", inorder_traversal(bst))  # 預期 [1, 2, 3, 4, 5, 6, 7]

後序遍歷 (Postorder Traversal)

def postorder_traversal(tree, i=0):
    if i >= len(tree) or tree[i] is None:
        return []
    return postorder_traversal(tree, 2 * i + 1) + postorder_traversal(tree, 2 * i + 2) + [tree[i]]

print("後序遍歷:", postorder_traversal(bst))  # 預期 [1, 2, 4, 3, 6, 7, 5]

(4) 樹的高度 (Height)

樹的高度可以通過計算最深層次來實現。這裡使用遞迴求得最大深度。

def height(tree, i=0):
    if i >= len(tree) or tree[i] is None:
        return 0
    left_height = height(tree, 2 * i + 1)
    right_height = height(tree, 2 * i + 2)
    return 1 + max(left_height, right_height)

print("樹的高度:", height(bst))  # 預期 4

(5) 樹的直徑(最大距離)

樹的直徑是樹中兩節點間的最大距離,可以用遞迴方式求取左右子樹高度和,然後取最大值。

直徑函式

def diameter(tree):
    def _diameter_and_height(i):
        if i >= len(tree) or tree[i] is None:
            return 0, 0  # 返回 (高度, 直徑)

        left_height, left_diameter = _diameter_and_height(2 * i + 1)
        right_height, right_diameter = _diameter_and_height(2 * i + 2)

        current_height = 1 + max(left_height, right_height)
        current_diameter = left_height + right_height
        max_diameter = max(current_diameter, left_diameter, right_diameter)

        return current_height, max_diameter

    _, tree_diameter = _diameter_and_height(0)
    return tree_diameter

print("樹的直徑(最大距離):", diameter(bst))  # 預期 4

完整統整程式碼

以下是完整程式碼,整合了上述所有功能:

# 插入新值
def insert(tree, value):
    if not tree:
        tree.append(value)
        return tree

    i = 0
    while i < len(tree):
        if value < tree[i]:
            left_child = 2 * i + 1
            if left_child >= len(tree):
                tree.extend([None] * (left_child - len(tree) + 1))
            if tree[left_child] is None:
                tree[left_child] = value
                return tree
            else:
                i = left_child
        else:
            right_child = 2 * i + 2
            if right_child >= len(tree):
                tree.extend([None] * (right_child - len(tree) + 1))
            if tree[right_child] is None:
                tree[right_child] = value
                return tree
            else:
                i = right_child

# 搜尋特定值
def search(tree, value):
    i = 0
    while i < len(tree) and tree[i] is not None:
        if tree[i] == value:
            return True
        elif value < tree[i]:
            i = 2 * i + 1
        else:
            i = 2 * i + 2
    return False

# 前序遍歷
def preorder_traversal(tree, i=0):
    if i >= len(tree) or tree[i] is None:
        return []
    return [tree[i]] + preorder_traversal(tree, 2 * i + 1) + preorder_traversal(tree, 2 * i +

 2)

# 中序遍歷
def inorder_traversal(tree, i=0):
    if i >= len(tree) or tree[i] is None:
        return []
    return inorder_traversal(tree, 2 * i + 1) + [tree[i]] + inorder_traversal(tree, 2 * i + 2)

# 後序遍歷
def postorder_traversal(tree, i=0):
    if i >= len(tree) or tree[i] is None:
        return []
    return postorder_traversal(tree, 2 * i + 1) + postorder_traversal(tree, 2 * i + 2) + [tree[i]]

# 計算樹高
def height(tree, i=0):
    if i >= len(tree) or tree[i] is None:
        return 0
    left_height = height(tree, 2 * i + 1)
    right_height = height(tree, 2 * i + 2)
    return 1 + max(left_height, right_height)

# 計算樹的直徑
def diameter(tree):
    def _diameter_and_height(i):
        if i >= len(tree) or tree[i] is None:
            return 0, 0

        left_height, left_diameter = _diameter_and_height(2 * i + 1)
        right_height, right_diameter = _diameter_and_height(2 * i + 2)

        current_height = 1 + max(left_height, right_height)
        current_diameter = left_height + right_height
        max_diameter = max(current_diameter, left_diameter, right_diameter)

        return current_height, max_diameter

    _, tree_diameter = _diameter_and_height(0)
    return tree_diameter

# 測試二元搜尋樹功能
bst = []
for value in [5, 3, 2, 7, 1, 6, 4]:
    bst = insert(bst, value)

print("搜尋 5:", search(bst, 5))  # 預期 True
print("搜尋 2:", search(bst, 2))  # 預期 True
print("搜尋 8:", search(bst, 8))  # 預期 False

print("前序遍歷:", preorder_traversal(bst))   # 預期 [5, 3, 2, 1, 4, 7, 6]
print("中序遍歷:", inorder_traversal(bst))    # 預期 [1, 2, 3, 4, 5, 6, 7]
print("後序遍歷:", postorder_traversal(bst))  # 預期 [1, 2, 4, 3, 6, 7, 5]

print("樹的高度:", height(bst))               # 預期 4
print("樹的直徑(最大距離):", diameter(bst))  # 預期 5

此程式實現了插入、搜尋、遍歷、計算樹高及樹直徑的所有功能,並以一維串列模擬了二元搜尋樹的結構。

沒有留言:

張貼留言