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

