用Python實現數據結構之二叉搜索樹
二叉搜索樹
二叉搜索樹是一種特殊的二叉樹,它的特點是:
-
對於任意一個節點p,存儲在p的左子樹的中的所有節點中的值都小於p中的值
-
對於任意一個節點p,存儲在p的右子樹的中的所有節點中的值都大於p中的值
一個圖例:
基於二叉搜索樹的這種關系,我們可以用它來實現有序映射
遍歷二叉搜索樹
基於二叉搜索樹的特性,采用中序遍歷的方式可以使得遍歷結果是按照從小到大的順序排列的。了解中序遍歷可以參考用Python實現數據結構之樹
這裏還需要思考的一個內容是在基於中序遍歷的前提下,如何求一個節點的後繼節點或前驅節點。
顯然這就是求比該節點大的下一個節點或比它小的前一個節點。我們拿求後繼節點為例:
-
當該節點有右孩子時,那麽後繼結點就是右子樹中最左邊的節點
-
當該節點沒有右孩子時,那麽後繼節點就是第一個是左孩子的祖先的父節點
算法用偽代碼表示為:
def after(p):
"""尋找二叉搜索樹的後繼節點的偽代碼"""
if right(p) is not None:
walk = right(p)
while left(right(p)) is not None: # 找最左
walk = left(walk)
return walk
else :
walk = p
ancestor = parent(walk)
while ancestor is not None and walk == right(ancestor): # 當walk是左孩子時或walk是根節點時停止
walk = ancestor
ancestor = parent(walk)
return ancestor
找前驅同理
搜索
既然叫做二叉搜索樹,那它很重要的一個用途就是搜索,搜索的方式為:
-
與根節點比較,如果相等則根節點就是要搜索的位置
-
比根節點小,則遞歸的與左孩子比較
-
比根節點大,則遞歸的與有孩子比較
-
如果最後沒找到,則返回最後一個與之比較的節點,意味著可以在這個節點位置插入剛才搜索的值
算法用偽代碼表示為:
def search(T,p,k):
"""二叉樹搜索的偽代碼,k是要搜索的值"""
if k == p.key():
return p
elif k < p.key() and T.left(p) is not None:
return search(T,T.left(p))
elif k > p.key() and T.right(p) is not None:
return search(T,T.right(p))
return p
搜索的時間與高度有關,是O(h),也就是最壞的情況下為O(n),最好的情況下是O(log(n))
插入
插入算法較簡單,它依賴於搜索算法,將搜索的返回的位置的值與key進行比較,
-
如果相等,則就在此位置修改
-
如果小於返回位置的值,則插入到返回位置的左孩子
-
如果小於返回位置的值,則插入到返回位置的右孩子
刪除
刪除操作較為復雜,因為刪除的位置可以是任意的位置,設刪除的位置為p
-
如果刪除的位置沒有孩子,則直接刪了就行
-
如果刪除的位置有一個孩子,則刪除之後把它的孩子接到它原來的位置上
-
如果刪除的位置有兩個孩子,則:
1.先找到位置p的前驅r,前驅在左子樹中
2.把p刪除,將r代替p
3.把r原來的位置刪除
使用前驅的原因是它必然比p的右子樹的所有節點小,也必然比除了r的p的左子樹的所有節點大
python實現
我們利用二叉樹來實現有序映射
class OrderedMap(BinaryTree,MutableMapping):
"""使用二叉搜索樹實現的有序映射"""
class _item():
def __init__(self, key, value):
self.key = key
self.value = value
def __eq__(self, other):
return self.key == other.key
def __ne__(self, other):
return self.key != other.key
def __lt__(self, other):
return self.key < other.key
class Position(BinaryTree.Position):
def key(self):
return self.element().key
def value(self):
return self.element().value
BinaryTree是在之前文章中定義的二叉樹類,具體參考用Python實現數據結構之樹
首先定義了兩個內嵌類,一個表示鍵值項,一個用於封裝節點
然後定義些非公開方法用於其他方法使用:
def _subtree_search(self, p, k):
"""搜索算法"""
if k == p.key():
return p
elif k < p.key():
if self.left(p) is not None:
return self._subtree_search(self.left(p), k)
else:
if self.right(p) is not None:
return self._subtree_search(self.right(p), k)
return p
def _subtree_first_position(self, p):
"""返回樹的最左節點"""
walk = p
while self.left(walk) is not None:
walk = self.left(walk)
return walk
def _subtree_last_position(self, p):
"""返回樹的最右節點"""
walk = p
while self.right(walk) is not None:
walk = self.right(walk)
return walk
下面是一些公開的訪問方法:
def first(self):
return self._subtree_first_position(
self.root()) if len(self) > 0 else None
def last(self):
return self._subtree_last_position(
self.root()) if len(self) > 0 else None
def before(self, p):
"""前驅位置"""
if self.left(p):
return self._subtree_last_position(self.left(p))
else:
walk = p
above = self.parent(walk)
while above is not None and walk == self.left(above):
walk = above
above = self.parent(walk)
return above
def after(self, p):
"""後繼位置"""
if self.right(p):
return self._subtree_first_position(self.right(p))
else:
walk = p
above = self.parent(walk)
while above is not None and walk == self.right(above):
walk = above
above = self.parent(walk)
return above
def find_position(self,k):
if self.is_empty():
return None
else:
p = self._subtree_search(self.root(),k)
return p
接下來是映射的核心方法:
def __getitem__(self, k):
if self.is_empty():
raise KeyError(‘Key Error‘+repr(k))
else:
p = self._subtree_search(self.root(),k)
if k!=p.key():
raise KeyError(‘Key Error‘ + repr(k))
return p.value()
def __setitem__(self, k,v):
if self.is_empty():
leaf = self.add_root(self._Item(k,v))
else:
p = self._subtree_search(self.root(),k)
if p.key() == k:
p.element().value = v
return
else:
item = self._Item(k,v)
if p.key() < k:
leaf = self.add_right(p,item)
else:
leaf = self.add_left(p, item)
def __iter__(self):
p = self.first()
while p is not None:
yield p.key()
p = self.after(p)
def mapdelete(self,p):
if self.left(p) and self.right(p): #兩個孩子都有的時候
replacement = self._subtree_last_position(self.left(p)) #用左子樹最右位置代替
self.replace(p,replacement.element())
p = replacement
self.delete(p)
def __delitem__(self, k):
if not self.is_empty():
p = self._subtree_search(self.root(),k)
if k == p.key():
self.mapdelete(p)
return
raise KeyError(‘Key Error‘ + repr(k))
最後是一些有序映射特有的方法:
def find_min(self):
"""找最小值,返回鍵值元組"""
if self.is_empty():
return None
else:
p = self.first()
return(p.key(), p.value())
def find_ge(self, k):
"""找第一個大於等於k的鍵值元組"""
if self.is_empty():
return None
else:
p = self.find_position(k)
if p.key() < k:
p = self.after(p)
return (p.key(), p.value()) if p is not None else None
def find_range(self, start, stop):
if not self.is_empty():
if start is None:
p = self.first()
else:
p = self.find_position(start)
if p.key() < start:
p = self.after(p)
while p is not None and (stop is None or p.key() < stop):
yield (p.key(), p.value())
p = self.after(p)
?
用Python實現數據結構之二叉搜索樹