Python實現(xiàn)二叉搜索樹增刪改查
引文
二叉搜索樹(Binary Search Tree,BST)是一種特殊的二叉樹,它滿足以下性質(zhì):
- 每個節(jié)點的值都大于其左子樹中的任何值,小于其右子樹中的任何值。
- 每個子樹也是一個二叉搜索樹。
二叉搜索樹有很多優(yōu)點,例如:
- 它可以快速地查找、插入和刪除元素。
- 它可以用來實現(xiàn)排序、范圍查詢、最大值和最小值等操作。
- 它可以用來構(gòu)建平衡樹、紅黑樹等高效的數(shù)據(jù)結(jié)構(gòu)。
在本文中,我將介紹如何用Python語言實現(xiàn)一個簡單的二叉搜索樹,并展示它的基本操作和應(yīng)用。
一、定義節(jié)點類
要實現(xiàn)一個二叉搜索樹,我們首先需要定義一個節(jié)點類,用來表示樹中的每個元素。一個節(jié)點類包含以下屬性:
- value:節(jié)點的值,可以是任意類型。
- left:節(jié)點的左子節(jié)點,如果沒有則為None。
- right:節(jié)點的右子節(jié)點,如果沒有則為None。
我們可以用以下代碼定義一個節(jié)點類:
class Node: def __init__(self, value): self.value = value self.left = None self.right = None
二、定義二叉搜索樹類
接下來,我們需要定義一個二叉搜索樹類,用來表示整個樹結(jié)構(gòu)。一個二叉搜索樹類包含以下屬性:
- root:樹的根節(jié)點,如果為空則為None。
我們可以用以下代碼定義一個二叉搜索樹類:
class BST: def __init__(self): self.root = None
三、實現(xiàn)插入操作
要向二叉搜索樹中插入一個元素,我們需要遵循以下步驟:
- 如果樹為空,則創(chuàng)建一個新節(jié)點作為根節(jié)點,并返回。
- 如果樹不為空,則從根節(jié)點開始,比較要插入的值和當(dāng)前節(jié)點的值。
- 如果要插入的值小于當(dāng)前節(jié)點的值,則遞歸地在當(dāng)前節(jié)點的左子樹中插入。
- 如果要插入的值大于當(dāng)前節(jié)點的值,則遞歸地在當(dāng)前節(jié)點的右子樹中插入。
- 如果要插入的值等于當(dāng)前節(jié)點的值,則不做任何操作,并返回。
我們可以用以下代碼實現(xiàn)插入操作:
def insert(self, value): # 創(chuàng)建一個新節(jié)點 new_node = Node(value) # 如果樹為空,則將新節(jié)點設(shè)為根節(jié)點 if self.root is None: self.root = new_node return # 否則從根節(jié)點開始遍歷 current = self.root while True: # 如果要插入的值小于當(dāng)前節(jié)點的值,則向左走 if value < current.value: # 如果左子節(jié)點為空,則將新節(jié)點設(shè)為左子節(jié)點 if current.left is None: current.left = new_node return # 否則繼續(xù)向左走 else: current = current.left # 如果要插入的值大于當(dāng)前節(jié)點的值,則向右走 elif value > current.value: # 如果右子節(jié)點為空,則將新節(jié)點設(shè)為右子節(jié)點 if current.right is None: current.right = new_node return # 否則繼續(xù)向右走 else: current = current.right # 如果要插入的值等于當(dāng)前節(jié)點的值,則不做任何操作,并返回 else: return
四、實現(xiàn)查找操作
要在二叉搜索樹中查找一個元素,我們需要遵循以下步驟:
- 如果樹為空,則返回False。
- 如果樹不為空,則從根節(jié)點開始,比較要查找的值和當(dāng)前節(jié)點的值。
- 如果要查找的值小于當(dāng)前節(jié)點的值,則遞歸地在當(dāng)前節(jié)點的左子樹中查找。
- 如果要查找的值大于當(dāng)前節(jié)點的值,則遞歸地在當(dāng)前節(jié)點的右子樹中查找。
- 如果要查找的值等于當(dāng)前節(jié)點的值,則返回True。
我們可以用以下代碼實現(xiàn)查找操作:
def search(self, value): # 如果樹為空,則返回False if self.root is None: return False # 否則從根節(jié)點開始遍歷 current = self.root while current is not None: # 如果要查找的值小于當(dāng)前節(jié)點的值,則向左走 if value < current.value: current = current.left # 如果要查找的值大于當(dāng)前節(jié)點的值,則向右走 elif value > current.value: current = current.right # 如果要查找的值等于當(dāng)前節(jié)點的值,則返回True else: return True # 如果遍歷完畢還沒有找到,則返回False return False
五、實現(xiàn)刪除操作
要在二叉搜索樹中刪除一個元素,我們需要遵循以下步驟:
- 如果樹為空,則返回None。
- 如果樹不為空,則從根節(jié)點開始,比較要刪除的值和當(dāng)前節(jié)點的值。
- 如果要刪除的值小于當(dāng)前節(jié)點的值,則遞歸地在當(dāng)前節(jié)點的左子樹中刪除,并將刪除后的左子樹設(shè)為當(dāng)前節(jié)點的左子節(jié)點。
- 如果要刪除的值大于當(dāng)前節(jié)點的值,則遞歸地在當(dāng)前節(jié)點的右子樹中刪除,并將刪除后的右子樹設(shè)為當(dāng)前節(jié)點的右子節(jié)點。
- 如果要刪除的值等于當(dāng)前節(jié)點的值,則分以下三種情況處理:
- 如果當(dāng)前節(jié)點沒有子節(jié)點,則直接刪除該節(jié)點,并返回None。
- 如果當(dāng)前節(jié)點只有一個子節(jié)點,則直接用該子節(jié)點替換該節(jié)點,并返回該子節(jié)點。
- 如果當(dāng)前節(jié)點有兩個子節(jié)點,則先在其右子樹中找到最小值(或者在其左子樹中找到最大值),然后用該最?。ɑ蜃畲螅┲堤鎿Q該節(jié)點,并遞歸地在其右(或左)子樹中刪除該最小(或最大)值,并將刪除后的右(或左)子樹設(shè)為該節(jié)點的右(或左)子節(jié)點。
我們可以用以下代碼實現(xiàn)刪除操作:
def delete(self, value): # 如果樹為空,則返回None if self.root is None: return None # 否則從根節(jié)點開始遍歷 current = self.root parent = None while current is not None and current.value != value: # 記錄父節(jié)點 parent = current # 如果要刪除的值小于當(dāng)前節(jié)點的值,則向左走 if value < current.value: current = current.left # 如果要刪除的值大于當(dāng)前節(jié)點的值,則向右走 else: current = current.right # 如果沒有找到要刪除的元素,則返回None if current is None: return None # 定義一個輔助函數(shù),用來找到一棵樹中最?。ɑ蜃畲螅┰厮诘慕Y(jié)點和其父結(jié)點,參數(shù)是根結(jié)點和一個布爾變量,表示是否尋找最小元素,默認(rèn)為True,如果為False則尋找最大元素 def find_min_max(node, is_min=True): # 初始化最小(或最大)結(jié)點和其父結(jié)點 min_max = node parent = None # 如果尋找最小元素,則一直向左走,否則一直向右走 if is_min: while min_max.left is not None: parent = min_max min_max = min_max.left else: while min_max.right is not None: parent = min_max min_max = min_max.right # 返回最?。ɑ蜃畲螅┙Y(jié)點和其父結(jié)點 return min_max, parent # 如果當(dāng)前結(jié)點沒有子結(jié)點,則直接刪除該結(jié)點,并返回None if current.left is None and current.right is None: # 如果是根結(jié)點,則將根結(jié)點設(shè)為None if parent is None: self.root = None # 否則判斷是父結(jié)點的左子結(jié)點還是右子結(jié)點,并將其設(shè)為None else: if parent.left == current: parent.left = None else: parent.right = None return None # 如果當(dāng)前結(jié)點只有一個子結(jié)點,則直接用該子結(jié)點替換該結(jié)點,并返回該子結(jié)點 if current.left is None or current.right is None: # 找到該子結(jié)點,可以是左子結(jié)點也可以是右子結(jié)點 child = current.left if current.left is not None else current.right # 如果是根結(jié)點,則將根結(jié)點設(shè)為該子結(jié)點 if parent is None: self.root = child # 否則判斷是父結(jié)點的左子結(jié)點還是右子結(jié)點,并將其設(shè)為該子結(jié)點 else: if parent.left == current: parent.left = child else: parent.right = child return child # 如果當(dāng)前結(jié)點有兩個子結(jié)點,則先在其右子樹中找到最小值(或者在其左子樹中找到最大值),然后用該最?。ɑ蜃畲螅┲堤鎿Q該結(jié)點,并遞歸地在其右(或左)子樹中刪除該最小(或最大)值,并將刪除后的右(或左)子樹設(shè)為該結(jié)點的右(或左)子節(jié)點 # 這里我們選擇在右子樹中找到最小值,也可以在左子樹中找到最大值,只要保證替換后的結(jié)點滿足二叉搜索樹的性質(zhì)即可 min_node, min_parent = find_min_max(current.right, is_min=True) # 用最小值替換當(dāng)前結(jié)點的值 current.value = min_node.value # 遞歸地在右子樹中刪除最小值,并將刪除后的右子樹設(shè)為當(dāng)前結(jié)點的右子節(jié)點 current.right = self.delete(min_node.value) return current
六、實現(xiàn)遍歷操作
遍歷是一種訪問樹中所有元素的方法,有三種常見的遍歷方式:
- 前序遍歷(Pre-order Traversal):先訪問根節(jié)點,再訪問左子樹,最后訪問右子樹。
- 中序遍歷(In-order Traversal):先訪問左子樹,再訪問根節(jié)點,最后訪問右子樹。
- 后序遍歷(Post-order Traversal):先訪問左子樹,再訪問右子樹,最后訪問根節(jié)點。
我們可以用以下代碼實現(xiàn)遍歷操作:
def pre_order(self, node): # 如果結(jié)點為空,則返回 if node is None: return # 先訪問根節(jié)點,打印其值 print(node.value, end=" ") # 再訪問左子樹,遞歸調(diào)用前序遍歷函數(shù) self.pre_order(node.left) # 最后訪問右子樹,遞歸調(diào)用前序遍歷函數(shù) self.pre_order(node.right) def in_order(self, node): # 如果結(jié)點為空,則返回 if node is None: return # 先訪問左子樹,遞歸調(diào)用中序遍歷函數(shù) self.in_order(node.left) # 再訪問根節(jié)點,打印其值 print(node.value, end=" ") # 最后訪問右子樹,遞歸調(diào)用中序遍歷函數(shù) self.in_order(node.right) def post_order(self, node): # 如果結(jié)點為空,則返回 if node is None: return # 先訪問左子樹,遞歸調(diào)用后序遍歷函數(shù) self.post_order(node.left) # 再訪問右子樹,遞歸調(diào)用后序遍歷函數(shù) self.post_order(node.right) # 最后訪問根節(jié)點,打印其值 print(node.value, end=" ")
七、實現(xiàn)其他操作
除了上述基本操作外,二叉搜索樹還可以實現(xiàn)一些其他有用的操作,例如:
- 求樹的高度(Height):從根節(jié)點到葉節(jié)點的最長路徑上的節(jié)點數(shù)。
- 求樹的大?。⊿ize):樹中所有節(jié)點的個數(shù)。
- 求第k?。ɑ虻趉大)元素:按照中序遍歷的順序,找到第k個元素。
- 求兩個元素的最近公共祖先(Lowest Common Ancestor):兩個元素在樹中的最低層次的公共祖先節(jié)點。
我們可以用以下代碼實現(xiàn)這些操作:
def height(self, node): # 如果結(jié)點為空,則返回0 if node is None: return 0 # 否則返回左右子樹中較大的高度加1 return max(self.height(node.left), self.height(node.right)) + 1 def size(self, node): # 如果結(jié)點為空,則返回0 if node is None: return 0 # 否則返回左右子樹的大小之和加1 return self.size(node.left) + self.size(node.right) + 1 def kth_smallest(self, node, k): # 如果結(jié)點為空,則返回None if node is None: return None # 定義一個輔助函數(shù),用來計算一棵樹中有多少個小于等于給定值的元素,參數(shù)是根結(jié)點和給定值 def count_less_equal(node, value): # 如果結(jié)點為空,則返回0 if node is None: return 0 # 如果結(jié)點的值小于等于給定值,則返回左子樹的個數(shù)加右子樹中小于等于給定值的個數(shù)加1 if node.value <= value: return count_less_equal(node.left, value) + count_less_equal(node.right, value) + 1 # 如果結(jié)點的值大于給定值,則返回左子樹中小于等于給定值的個數(shù) else: return count_less_equal(node.left, value) # 計算當(dāng)前結(jié)點左子樹中有多少個元素 left_count = self.size(node.left) # 如果左子樹中有k-1個元素,則當(dāng)前結(jié)點就是第k小元素,返回其值 if left_count == k - 1: return node.value # 如果左子樹中有超過k-1個元素,則第k小元素在左子樹中,遞歸調(diào)用函數(shù)在左子樹中查找 elif left_count > k - 1: return self.kth_smallest(node.left, k) # 如果左子樹中有少于k-1個元素,則第k小元素在右子樹中,遞歸調(diào)用函數(shù)在右子樹中查找,注意要更新k的值為k-left_count-1,因為已經(jīng)排除了左子樹和當(dāng)前結(jié)點 else: return self.kth_smallest(node.right, k - left_count - 1) def lowest_common_ancestor(self, node, p, q): # 如果結(jié)點為空,則返回None if node is None: return None # 如果結(jié)點的值等于p或q中的任意一個,則返回該結(jié)點,表示找到了其中一個元素 if node.value == p or node.value == q: return node # 否則在左右子樹中分別查找p和q,得到兩個結(jié)果 left_result = self.lowest_common_ancestor(node.left, p, q) right_result = self.lowest_common_ancestor(node.right, p, q) # 如果兩個結(jié)果都不為空,則表示p和q分別在當(dāng)前結(jié)點的兩側(cè),那么當(dāng)前結(jié)點就是最近公共祖先,返回該結(jié)點 if left_result is not None and right_result is not None: return node # 如果兩個結(jié)果中有一個為空,則表示p和q都在另一個結(jié)果所在的子樹中,那么返回非空的結(jié)果作為最近公共祖先 if left_result is None: return right_result else: return left_result
到此這篇關(guān)于Python實現(xiàn)二叉搜索樹增刪改查的文章就介紹到這了,更多相關(guān)Python 二叉搜索樹增刪改查內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
總結(jié)分析python數(shù)據(jù)化運營關(guān)聯(lián)規(guī)則
本文內(nèi)容主要介紹了python數(shù)據(jù)化運營中關(guān)聯(lián)規(guī)則的一般應(yīng)用場景,以及關(guān)聯(lián)規(guī)則的實現(xiàn),并例舉了適應(yīng)的應(yīng)用示例,方便大家更直觀的理解應(yīng)用2021-08-08Python+Selenium自動化環(huán)境搭建與操作基礎(chǔ)詳解
Selenium是如今最常用的自動化測試工具之一,支持快速開發(fā)自動化測試框架,且支持在多種瀏覽器上執(zhí)行測試。本文將介紹關(guān)于Selenium?Python自動化腳本環(huán)境搭建的相關(guān)資料,需要的朋友可以參考下2022-03-03pandas or sql計算前后兩行數(shù)據(jù)間的增值方法
下面小編就為大家分享一篇pandas or sql計算前后兩行數(shù)據(jù)間的增值方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-04-04