小白專場-是否同一顆二叉搜尋樹-python語言實現
目錄
- 一、二叉搜尋樹的相同判斷
- 二、問題引入
- 三、舉例分析
- 四、方法探討
- 4.1 中序遍歷
- 4.2 層序遍歷
- 4.3 先序遍歷
- 4.4 後序遍歷
- 五、總結
- 六、程式碼實現
更新、更全的《資料結構與演算法》的更新網站,更有python、go、人工智慧教學等著你:https://www.cnblogs.com/nickchen121/p/11407287.html
一、二叉搜尋樹的相同判斷
二叉搜尋樹是一種特殊的二叉樹,在一定程度上是基於二分查詢思想產生的,在它的任何一個節點node處,node的左子樹中的所有元素都比node本身的數值要小,而node的右子樹中的所有元素都比node本身要大。
二、問題引入
與普通的二叉樹不同,任意給一串不重複的數字,就可以確定一棵二叉搜尋樹,例如:當給定序列12,5,11,17,16,19,18時,可以確定的二叉搜尋樹如下:
本質上,由於二叉搜尋樹的節點左右按大小分界的性質,確定一棵二叉搜尋樹最重要的還是序列中數字出現的次序,一個小小的調換就可能導致二叉搜尋樹改變,因此,有時候我們需要找到一些方法來確認兩個序列所對應的二叉搜尋樹是否為同一棵二叉搜尋樹。
三、舉例分析
我們以最簡單的例子,由1,2,3構成的6種序列,及其對應的二叉搜尋樹、先序、中序、後序、層序遍歷結果分別如下:
四、方法探討
我們可能會自然地想到用遍歷對兩顆二叉搜尋樹進行比較,常用的遍歷方法:
先序遍歷、中序遍歷、後序遍歷、層序遍歷
下面我們將分點探討他們的效果。
4.1 中序遍歷
中序遍歷總是先遍歷當前節點node的左子節點,然後遍歷自身元素、之後遍歷右子節點。因而,在二叉搜尋樹中,中序遍歷的結果總是所有元素的升序排列(如上表中標紅的一行所示);換句話說,中序遍歷結果是不能幫助我們判斷兩個序列是否對應同一棵二叉搜尋樹的。
4.2 層序遍歷
- 由於二叉搜尋樹的構建方式與元素在每一層的分佈密切相關,對於出現元素集合完全相同的兩個不同的序列,它們各自所對應的二叉搜尋樹如果在某一層開始出現的元素不相同,那麼它們不是同一棵二叉搜尋樹。
- 這也可以由遞迴的思想來理解,對於特定的某一層來說,由於前一層已經確定,那麼將序列中剩下的元素按先後順序插入樹中的合適位置時,其位置也就自動被唯一確定了,每個元素是否能在某一層出現以及在該元素某一層出現的位置是確定死了的,完全由上一層的元素來決定,不存在其他的位置來放置它。
- 再者,如果給我們了一棵二叉搜尋樹的層序遍歷結果,我們會發現是可以還原出一整棵二叉搜尋樹的,這與普通二叉樹有些區別。
因此可以由層序遍歷結果完成我們的判斷。
4.3 先序遍歷
根據一個先序遍歷結果(不妨仍以上面的那棵二叉搜尋樹為例,其先序遍歷結果是12,5,11,17,16,19,18),從根節點12開始,根據與12的大小關係,12後面的5和11肯定在左子樹中,而17,16,19,18肯定在12的右子樹中;在5和11中,根據順序,5應作為12左子樹的根節點,然後11比5大,所以11是5的右子節點,這樣左子樹判斷完畢;在左子樹17,16,19,18中,17是根節點,16是17的左子節點、19和18是17的右子樹,其中19是17的右子節點,18是19的左子節點。這樣我們就完成了整個二叉搜尋樹的構建。
整合一下,我們看到,在二叉搜尋樹的概念下,先序遍歷的結果可以逐步“二分”,分成比node小和比node大的兩部分,分別構成node的左右子樹;然後以相同的思路進行二分,即可完成構建。
4.4 後序遍歷
後序遍歷與先序遍歷本質上一樣,只是在邏輯上進行了完全翻轉(注意不是簡單的序列翻轉),思路和先序遍歷基本相同,仍然需要遞迴分組,只是需要從遍歷結果的最後一個元素開始反向構建。
五、總結
由前述分析討論可見,在二叉搜尋樹的概念之下,中序遍歷損失的資訊量是最大的,但由於它得到的結果的升序特性,這是我們可以用它來判斷一棵二叉樹是否是二叉搜尋樹,這是其他幾種遍歷無法做到的,這也表示了中序遍歷與二叉搜尋樹結合得很緊密。
而層序、先序、後序遍歷都可以用來判斷兩個給定序列是否為同一棵二叉搜尋樹(這裡以先序遍歷結果實現如下)。
六、程式碼實現
# python語言實現
# Judge the same binary search tree
# 由於二叉搜尋樹不同於二叉樹,僅根據二叉搜尋樹的先序遍歷結果就能判定二叉樹的唯一形狀;
# 因此我們可以通過比較兩個二叉搜尋樹的先序遍歷結果來判斷他們是否為同一個二叉搜尋樹;
class node:
def __init__(self, elem=None, lchild=None, rchild=None):
self.elem = elem
self.lchild = lchild
self.rchild = rchild
class tree:
def __init__(self, root=node()):
self.root = root
def bi_search_tree_establish(List): # 根據輸入的列表建立二叉搜尋樹
if List:
mytree = tree(node(List[0]))
for i in range(1, len(List)):
temp_node = mytree.root
while temp_node:
if List[i] < temp_node.elem:
if temp_node.lchild:
temp_node = temp_node.lchild
else:
temp_node.lchild = node(List[i])
break
else:
if temp_node.rchild:
temp_node = temp_node.rchild
else:
temp_node.rchild = node(List[i])
break
return mytree
else:
return None
def preorder_probing(now_node, pre_L): # 先序遍歷——遞迴實現
# print(now_node.elem)
pre_L.append(now_node.elem)
if now_node.lchild:
preorder_probing(now_node.lchild, pre_L)
if now_node.rchild:
preorder_probing(now_node.rchild, pre_L)
def cmp(a, b): # 比較序列函式,Python3的常用函式庫裡已經沒有cmp函數了
leng = len(a)
if leng == len(b):
for i in range(0, leng):
if a[i] != b[i]:
# print("False")
return -1
# print("True")
return 0
else:
# print("False")
return -1
if __name__ == "__main__":
N = int(input()) # 輸入n表示需要檢測的組數
S_List = [int(i) for i in input()] # 輸入一個二叉搜尋樹序列作為標準,與下面的進行比較
S_Tree = bi_search_tree_establish(S_List) # 構建標準二叉搜尋樹
if S_Tree:
S_pre_list = []
preorder_probing(S_Tree.root, S_pre_list)
for i in range(0, N):
List = [int(i) for i in input()] # 輸入待比較的二叉搜尋樹序列
MyTree = bi_search_tree_establish(List) # 構建待比較二叉搜尋樹
if MyTree:
pre_list = []
preorder_probing(MyTree.root, pre_list)
if cmp(S_pre_list, pre_list) == 0:
print("YES")
else:
print("NO")