二叉搜尋樹的python實現

james發表於2019-02-16

二叉查詢樹或者是一棵空樹,或者是具有下列性質的二叉樹: 若它的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值; 若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值; 它的左、右子樹也分別為二叉排序樹。
二叉搜尋樹不一定是完全二叉樹,因此不能像堆一樣可以陣列表示。
定義一個TNode類來例項化節點,有key,value,l_child,r_child屬性。其中key,value通過建構函式傳入,l_child,r_child初始化為None。

再定義一個BSTree類,根節點初始化為None。

然後給BSTree新增以下方法:
insert(key,value) 插入一個節點
update(key,value) 更新節點的值
search(key) 通過key來找相應節點的value
remove(key) 通過key來刪除樹中相應節點
以及深度優先和廣度優先的遍歷方法。

insert:
傳入引數key,value,插入一個新的節點。但是如果樹中已經存在key為key的節點,就將插入操作轉變為更新操作。

 def __insert(self, key, value, curr_node):
        if curr_node is None:
            return TNode(key, value)

        if key < curr_node.key:
            curr_node.l_child = self.__insert(key, value, curr_node.l_child)

        elif key > curr_node.key:
            curr_node.r_child = self.__insert(key, value, curr_node.r_child)

        else:
            curr_node.value = value

        return curr_node

噹噹前的節點為None時,根據引數中的key和value建立新的節點,並返回這個節點。當key值小於當前節點的key時,將新節點插入到當前節點的左子樹的相應位置,然後返回當前節點。同理key值大於當前節點的key時,將新節點插入到當前節點的右子樹的相應位置,返回當前節點。如果key等於當前節點key,將插入操作改變為更新操作。

search:

    def __search(self, node, key):
        if node:
            if node.key == key:
                return node
            elif node.key > key:
                return self.__search(node.l_child, key)
            else:
                return self.__search(node.r_child, key)

        return False

在當前node為根節點的子樹中,查詢key為key的子節點,如果當前node的key值與引數相同,返回當前節點。否則遞迴地到左右子樹中找key為key的節點,如果沒有找到,最終返回False。

update:

  def __update(self, key, value, node):
        if node:
            if node.key == key:
                node.value = value
            elif node.key > key:
                self.__update(key, value, node.l_child)
            else:
                self.__update(key, value, node.r_child)

在當前node為根節點的子樹中,查詢key為key的節點。如果當前node的key值與引數相同,更新當前節點的value。否則遞迴地到左右子樹中找到key為key的節點,並更新value。

remove:

執行remove操作時,分為兩種情況。
1.當前要刪除的節點沒有右子節點,此時將左子節點作為當前節點,返回。
2.當前要刪除的節點(node_to_delete)有右子節點(r_child),找到右子節點下,最小的節點(r_child_smallest_child),將該節點與要刪除的節點換位置。具體操作為:先拿到r_child_smallest_child,再將該節點從以r_child為根的子樹中刪除。再將node_to_delete的左孩子作為r_child_smallest_child的左孩子,node_to_delete的右孩子作為r_child_smallest_child的右孩子。最終將r_child_smallest_child賦給node_to_delete,並返回。

程式碼如下:

    def __remove(self, node, key):
        if node:
            if node.key < key:
                node.r_child = self.__remove(node.r_child, key)
            elif node.key > key:
                node.l_child = self.__remove(node.l_child, key)
            else:
                if node.r_child is None:
                    node = node.l_child
                else:
                    node_r_child_smallest_child = self.__get_smallest_child(node.r_child)
                    self.__remove_smallest(node.r_child)
                    node_r_child_smallest_child.r_child = node.r_child
                    node_r_child_smallest_child.l_child = node.l_child
                    node = node_r_child_smallest_child
            return node

        return False

其中__remove_smallest和__get_smallest_child含義分別是:刪除當前節點下,最小的節點,以及獲得當前節點下最小的節點。
程式碼如下:

    def __remove_smallest(self, node):
        if node.l_child:
            node.l_child = self.__remove_smallest(node.l_child)
            return node
        return node.r_child

噹噹前節點有左孩子時,刪除左子樹中最小的節點,並且返回當前節點。沒有左孩子時,返回右孩子。

    def __get_smallest_child(self, node):
        if node.l_child:
            return self.__get_smallest_child(node.l_child)
        return node

噹噹前節點有左孩子時,獲得左子樹中最小的節點。沒有左孩子時,返回當前節點。

深度優先遍歷,可以選擇先序遍歷,中序遍歷,後續遍歷,以其中之一為例:

    def __l_m_r(self, node):
        if node:
            self.__l_m_r(node.l_child)
            print(node)
            self.__l_m_r(node.r_child)

當進行廣度優先遍歷時,需要用到佇列。

    def bfs(self):
        queue = deque()
        queue.append(self.root)

        while queue:
            node = queue.popleft()
            if node:
                l_child = node.l_child
                r_child = node.r_child
                print(node.value)
                queue.append(l_child)
                queue.append(r_child)

以上是我所瞭解的二叉搜尋樹的基本功能

相關文章