一、二叉搜索樹的相同判斷
二叉搜索樹是一種特殊的二叉樹,在一定程度上是基於二分查找思想產生的,在它的任何一個節點node處,node的左子樹中的所有元素都比node本身的數值要小,而node的右子樹中的所有元素都比node本身要大。
問題引入
與普通的二叉樹不同,任意給一串不重複的數字,就可以確定一棵二叉搜索樹,例如:當給定序列12,11,5,17,16,19,18時,可以確定的二叉搜索樹如下:
本質上,由於二叉搜索樹的節點左右按大小分界的性質,確定一棵二叉搜索樹最重要的還是序列中數字出現的次序,一個小小的調換就可能導致二叉搜索樹改變,因此,有時候我們需要找到一些方法來確認兩個序列所對應的二叉搜索樹是否爲同一棵二叉搜索樹。
舉例分析
我們以最簡單的例子,由1,2,3構成的6種序列,及其對應的二叉搜索樹、先序、中序、後序、層序遍歷結果分別如下:
方法探討
我們可能會自然地想到用遍歷對兩顆二叉搜索樹進行比較,常用的遍歷方法:
先序遍歷、中序遍歷、後序遍歷、層序遍歷
下面我們將分點探討他們的效果:
中序遍歷
中序遍歷總是先遍歷當前節點node的左子節點,然後遍歷自身元素、之後遍歷右子節點。因而,在二叉搜索樹中,中序遍歷的結果總是所有元素的升序排列(如上表中標紅的一行所示);換句話說,中序遍歷結果是不能幫助我們判斷兩個序列是否對應同一棵二叉搜索樹的。
層序遍歷
(1)由於二叉搜索樹的構建方式與元素在每一層的分佈密切相關,對於出現元素集合完全相同的兩個不同的序列,它們各自所對應的二叉搜索樹如果在某一層開始出現的元素不相同,那麼它們不是同一棵二叉搜索樹。
(2)這也可以由遞歸的思想來理解,對於特定的某一層來說,由於前一層已經確定,那麼將序列中剩下的元素按先後順序插入樹中的合適位置時,其位置也就自動被唯一確定了,每個元素是否能在某一層出現以及在該元素某一層出現的位置是確定死了的,完全由上一層的元素來決定,不存在其他的位置來放置它。
(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的左右子樹;然後以相同的思路進行二分,即可完成構建。
後序遍歷
後序遍歷與先序遍歷本質上一樣,只是在邏輯上進行了完全翻轉(注意不是簡單的序列翻轉),思路和先序遍歷基本相同,仍然需要遞歸分組,只是需要從遍歷結果的最後一個元素開始反向構建。
總結
由前述分析討論可見,在二叉搜索樹的概念之下,中序遍歷損失的信息量是最大的,但由於它得到的結果的升序特性,這是我們可以用它來判斷一棵二叉樹是否是二叉搜索樹,這是其他幾種遍歷無法做到的,這也表示了中序遍歷與二叉搜索樹結合得很緊密。
而層序、先序、後序遍歷都可以用來判斷兩個給定序列是否爲同一棵二叉搜索樹(這裏以先序遍歷結果實現如下)。
代碼實現
# Judge the same binary search tree
# Edited by Ocean_Waver
# 由於二叉搜索樹不同於二叉樹,僅根據二叉搜索樹的先序遍歷結果就能判定二叉樹的唯一形狀;
# 因此我們可以通過比較兩個二叉搜索樹的先序遍歷結果來判斷他們是否爲同一個二叉搜索樹;
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")