1. 程式人生 > >python3 實現二叉查詢樹的搜尋、插入、刪除

python3 實現二叉查詢樹的搜尋、插入、刪除

1. 定義

二叉查詢樹(Binary Search Tree),又稱為二叉搜尋樹、二叉排序樹。其或者是一棵空樹;或者是具有以下性質的二叉樹:

  • 若左子樹不空,則左子樹上所有結點的值均小於或等於它的根結點的值
  • 若右子樹不空,則右子樹上所有結點的值均大於或等於它的根結點的值
  • 左、右子樹也分別為二叉排序樹

    圖 1
    圖 1


    樹中節點的定義如下:
class Node:
    def __init__(self, data):
        self.data = data
        self.lchild = None
        self.rchild = None

2. 查詢與插入

當二叉查詢樹不為空時:

  • 首先將給定值與根結點的關鍵字比較,若相等,則查詢成功
  • 若小於根結點的關鍵字值,遞迴查左子樹
  • 若大於根結點的關鍵字值,遞迴查右子樹
  • 若子樹為空,查詢不成功

二叉排序樹是一種動態樹表。其特點是:樹的結構通常不是一次生成的,而是在查詢過程中,當樹中不存在關鍵字等於給定值的結點時再進行插入。新插入的結點一定是一個新新增的葉子結點,並且是查詢不成功時查詢路徑上訪問的最後一個結點的左孩子或右孩子結點。如下圖所示:

這裡寫圖片描述

插入與搜尋的程式碼:

     # 搜尋
    def search(self, node, parent, data):
        if node is None:
            return False, node, parent
        if node.data == data:
            return True, node, parent
        if node.data > data:
            return self.search(node.lchild, node, data)
        else:
            return self.search(node.rchild, node, data)

    # 插入
    def insert(self, data):
        flag, n, p = self.search(self.root, self.root, data)
        if not flag:
            new_node = Node(data)
            if data > p.data:
                p.rchild = new_node
            else:
                p.lchild = new_node

3. 刪除

二叉查詢樹的刪除操作分為三種情況:

  1. 如果待刪除的節點是葉子節點,那麼可以立即被刪除,如下圖所示:

    這裡寫圖片描述

  2. 如果節點只有一個兒子,則將此節點parent的指標指向此節點的兒子,然後刪除節點,如下圖所示:

    這裡寫圖片描述

  3. 如果節點有兩個兒子,則將其右子樹的最小資料代替此節點的資料,並將其右子樹的最小資料刪除,如下圖所示:

    這裡寫圖片描述

刪除節點的程式碼:

    # 刪除
    def delete(self, root, data):
        flag, n, p = self.search(root, root, data)
        if flag is False:
            print "無該關鍵字,刪除失敗"
        else:
            if n.lchild is None:
                if n == p.lchild:
                    p.lchild = n.rchild
                else:
                    p.rchild = n.rchild
                del n
            elif n.rchild is None:
                if n == p.lchild:
                    p.lchild = n.lchild
                else:
                    p.rchild = n.lchild
                del n
            else:  # 左右子樹均不為空
                pre = n.rchild
                if pre.lchild is None:
                    n.data = pre.data
                    n.rchild = pre.rchild
                    del pre
                else:
                    next = pre.lchild
                    while next.lchild is not None:
                        pre = next
                        next = next.lchild
                    n.data = next.data
                    pre.lchild = next.rchild
                    del next
#注意此處沒有考慮BTS中只有一個根節點的情況。應該重新寫一個if進行判斷。
#if self.root==data:
#self.root=None
        

4. 遍歷

1. 先序遍歷

訪問順序如下:

  • 訪問根節點
  • 先序遍歷左子樹
  • 先序遍歷右子樹

以圖 1為例,訪問順序為:4,2,1,3,5,6

    # 先序遍歷
    def preOrderTraverse(self, node):
        if node is not None:
            print node.data,
            self.preOrderTraverse(node.lchild)
            self.preOrderTraverse(node.rchild)

2. 中序遍歷

訪問順序如下:

  • 中序遍歷左子樹
  • 訪問根節點
  • 中序遍歷右子樹

以圖 1為例,訪問順序為:1,2,3,5,7,9

    # 中序遍歷
    def inOrderTraverse(self, node):
        if node is not None:
            self.inOrderTraverse(node.lchild)
            print node.data,
            self.inOrderTraverse(node.rchild)

3. 後序遍歷

訪問順序如下:

  • 後序遍歷左子樹
  • 後序遍歷右子樹
  • 訪問根節點
    以圖 1為例,訪問順序為:1,3,2,9,7,5
    # 後序遍歷
    def postOrderTraverse(self, node):
        if node is not None:
            self.postOrderTraverse(node.lchild)
            self.postOrderTraverse(node.rchild)
            print node.data,

5. 完整程式碼

# encoding: utf-8
class Node:
    def __init__(self, data):
        self.data = data
        self.lchild = None
        self.rchild = None

class BST:
    def __init__(self, node_list):
        self.root = Node(node_list[0])
        for data in node_list[1:]:
            self.insert(data)

    # 搜尋
    def search(self, node, parent, data):
        if node is None:
            return False, node, parent
        if node.data == data:
            return True, node, parent
        if node.data > data:
            return self.search(node.lchild, node, data)
        else:
            return self.search(node.rchild, node, data)

    # 插入
    def insert(self, data):
        flag, n, p = self.search(self.root, self.root, data)
        if not flag:
            new_node = Node(data)
            if data > p.data:
                p.rchild = new_node
            else:
                p.lchild = new_node

    # 刪除
    def delete(self, root, data):
        flag, n, p = self.search(root, root, data)
        if flag is False:
            print "無該關鍵字,刪除失敗"
        else:
            if n.lchild is None:
                if n == p.lchild:
                    p.lchild = n.rchild
                else:
                    p.rchild = n.rchild
                del p
            elif n.rchild is None:
                if n == p.lchild:
                    p.lchild = n.lchild
                else:
                    p.rchild = n.lchild
                del p
            else:  # 左右子樹均不為空
                pre = n.rchild
                if pre.lchild is None:
                    n.data = pre.data
                    n.rchild = pre.rchild
                    del pre
                else:
                    next = pre.lchild
                    while next.lchild is not None:
                        pre = next
                        next = next.lchild
                    n.data = next.data
                    pre.lchild = next.rchild
                    del p


    # 先序遍歷
    def preOrderTraverse(self, node):
        if node is not None:
            print node.data,
            self.preOrderTraverse(node.lchild)
            self.preOrderTraverse(node.rchild)

    # 中序遍歷
    def inOrderTraverse(self, node):
        if node is not None:
            self.inOrderTraverse(node.lchild)
            print node.data,
            self.inOrderTraverse(node.rchild)

    # 後序遍歷
    def postOrderTraverse(self, node):
        if node is not None:
            self.postOrderTraverse(node.lchild)
            self.postOrderTraverse(node.rchild)
            print node.data,

a = [49, 38, 65, 97, 60, 76, 13, 27, 5, 1]
bst = BST(a)  # 建立二叉查詢樹
bst.inOrderTraverse(bst.root)  # 中序遍歷

bst.delete(bst.root, 49)
print
bst.inOrderTraverse(bst.root)