c语言判断一个已知的二叉树是否是二叉排序树_10584 二叉树怎样序列化才能重建...
「序列化」(serialization),指的是把復雜的數據結構轉化為線性結構,以方便存儲的過程。序列化得到的線性結構必須能重建出原有的結構,才有意義。
對于二叉樹,常用的序列化方法是在樹上進行某種遍歷(如先序、中序、后序、層序),用一種或兩種遍歷結果作為序列化結果。但并不是隨便選一種或兩種遍歷結果,都能把二叉樹重建出來。本文將給出二叉樹序列化能夠重建的一個充分條件,并且在實用中,這個條件也可以認為是必要的。
一、幾種常見的序列化方法
1.1 僅使用一種遍歷的序列化方法
這是最常見的序列化方法。可以采用的遍歷順序包括先序、后序、層序。在遍歷時,要把空指針也包含在遍歷的結果中。例如,對下圖的二叉樹,進行先序、后序、層序遍歷的結果分別為 12##3#4##、##2###431、123###4##(# 表示空指針)。
從這幾種遍歷的結果重建二叉樹的過程是顯然的,程序從略。其時間復雜度為 O(n),n 為樹中的結點數。
而僅根據(帶空指針的)中序遍歷,是不能重建二叉樹的。比如,上面這棵樹的中序遍歷為 #2#1#3#4#。事實上可以證明,任何一棵二叉樹的中序遍歷結果,都會是空指針與樹中結點交替出現的形式,所以空指針沒有提供任何額外的信息。
1.2 使用兩種遍歷的序列化方法
這是學習二叉樹時的常見思考題:如何根據兩種遍歷結果(不含空指針)重建二叉樹。并不是任意兩種遍歷結果都能重建二叉樹,我們先考慮一種可行的情況:已知先序和中序遍歷。
以上圖中的二叉樹為例,它的先序遍歷為 124536,中序遍歷為 425136。先序遍歷的第一個元素 1 一定是根,以這個元素為分界點把中序遍歷結果分成兩半,可以得到 425 和 36,這就是左子樹和右子樹各自的中序遍歷。在先序遍歷中取同樣長度的兩個子序列,得到 245 和 36,這就是兩個子樹的先序遍歷了。由此可以遞歸地重建出整棵二叉樹。Python 代碼如下(Node 類的定義從略):
def我們注意到,程序正常運行的一個條件是樹中沒有重復的元素。否則,在中序遍歷中就不一定能確定根的位置了。
另外,上面這個程序使用了 index 函數,它的時間復雜度不是 O(1) 的。在某些編程語言中,截取一個序列的子序列需要把子序列復制一份,這也不是 O(1) 的。這些因素導致整個程序的時間復雜度高于 O(n),在最壞情況下可以達到 O(n^2)。一個解決辦法是把兩個已知序列包裝成輸入流(如 Python 中的 collections.deque),在創建根結點時,暫時先不去找它在中序遍歷中的位置,而是遞歸下去,直到在中序遍歷中遇到根結點時再返回來。Python 代碼如下,其中 stop 參數表示在中序遍歷中遇到什么元素就該返回了。
from這段程序理解起來稍微困難一些,不過它更能揭示「先序 + 中序」與「先序 + 空指針」兩種序列化方式的相似之處。在這段程序中,中序遍歷的作用是用做遞歸終止條件,即告訴程序哪些地方應當是空指針。也就是說,「中序遍歷」與「空指針」提供的是相同的信息,只不過更間接一些。
現在來看其它使用兩種遍歷的序列化方法。「后序 + 中序」的情況跟「先序 + 中序」的情況是十分類似的,把上面兩段程序稍加修改,都可以用于「后序 + 中序」的重建(具體要修改哪里留作練習)。但已知先序和后序遍歷結果時,是不能重建二叉樹的,例如下面兩棵樹的先序遍歷都是 12,后序遍歷都是 21。
同時也可以發現,上圖中兩棵樹的層序遍歷也都是 12。因此,依靠「層序 + 先序」或「層序 + 后序」兩種遍歷結果,也都不能重建二叉樹。依靠「層序 + 中序」是可以重建的,具體方法將在下一小節最后說明。
1.3 二叉搜索樹(BST)的序列化方法
二叉搜索樹(binary search tree, BST)是這樣一種二叉樹:對任一結點,它的左子樹中所有結點都小于(或等于)本身,而右子樹中所有結點都大于(或等于)本身。BST 的定義不統一,有些定義不允許樹中有重復元素,有些定義允許各個結點的單側子樹中有等于本身的元素,有些定義允許兩側都有等于本身的元素。本文采用最寬松的定義,但本小節僅討論沒有重復元素的情況,至于這個條件的放寬,留到第三大節中討論。
如果已知一棵樹是 BST,那么只需要知道先序、后序、層序遍歷中的一者(不需要包含空指針)就足以重建了。這是因為,把這些遍歷結果排個序,就是中序遍歷,從而化歸成了上一小節的情況。這說明,BST 的順序與中序遍歷提供的是同樣的信息。當然,排序的時間復雜度是 O(n log n),高于重建的復雜度 O(n)。能不能繞過排序呢?也是可以的,只要我們能在重建過程中,根據「樹是 BST」這個條件得知哪些地方是空指針。
例如,在已知先序遍歷結果時,可以用如下方法重建 BST。inf 代表一個比所有元素都大的值。在上一小節里,我們用「中序遍歷的開頭就是上面某層的根結點(stop)」作為空指針的判斷條件。現在沒有中序遍歷了,但我們可以改用「先序遍歷的下一個結點大于等于 stop」作為空指針的判斷條件。
from已知后序遍歷時的重建方法與已知先序遍歷時類似,略。
在已知層序遍歷時,BST 的重建方法如下。在重建過程中,對于每個結點,記錄下以它為根的子樹中的元素的取值范圍(用開區間表示)。結合結點本身的值,就可以知道它的兩個子結點的取值范圍。如果先序遍歷中的下一個元素正好落在這些范圍之內,那么這些子結點就存在,否則這些子結點為空。
from下面解答上一小節的遺留問題:對于一棵普通二叉樹(非 BST),在已知層序和中序遍歷時,如何重建。事實上,中序遍歷可以認為是指定了樹中元素的順序關系,于是便化歸為剛剛解決的「已知層序遍歷重建 BST」的問題。在實現上,可以用一個哈希表將樹中的元素映射為它們在中序遍歷中的次序,然后就可以套用上一段程序解決。
二、二叉樹序列化能夠重建的充分條件
上文討論的所有情況,可以總結成如下的「定理」:
一棵二叉樹能夠被重建,如果滿足下面三個條件之一:a1. 已知先序遍歷;或
a2. 已知后序遍歷;或
a3. 已知層序遍歷;
且滿足下面三個條件之一:
b1. 前面已知的那種遍歷包含了空指針;或
b2. 已知中序遍歷,且樹中不含重復元素;或
b3. 樹是二叉搜索樹,且不含重復元素。
這是本文的主要結論。它指出,中序遍歷提供的信息,與空指針和 BST 是相同的,而與先序、后序、層序遍歷提供的信息互補。同時,這個充分條件也幾乎是必要的,因為如果僅滿足 a 組的兩個條件,或者僅滿足 b 組的兩個條件,都不能重建二叉樹。
三、「不含重復元素」的必要性探討
上一節給出的條件是個充分條件;導致它不是必要條件的原因,就在于 b2、b3 兩個條件中的「不含重復元素」。在這一節中,我們就來探討一下這個條件可以放寬到什么程度,還能保證重建出的樹是唯一的。
先看 b3,即 BST 的情況。對于 BST,可以把「不含重復元素」,放寬到「允許一側子樹中有與根相等的元素」。無論是根據先序還是層序遍歷重建 BST,關鍵步驟都是確定遍歷結果中的下一個元素應該長在樹的什么位置,而這是根據每個可能長出結點的位置(下圖中灰色的橢圓)允許的取值范圍確定的。在不允許重復元素的情況下,各個「生長點」的取值范圍都是開區間;在允許一側子樹中有與根相等的元素的情況下,各個「生長點」的取值范圍是半開半閉區間 —— 在這兩種情況下,各區間都沒有重疊,所以都可以唯一確定下一個元素應該長在哪里。但如果放寬到「兩側子樹都允許有與根相等的元素」,區間就變成了閉區間,端點出現重疊,就不能保證重建出的樹唯一了。例如,如果先序或層序遍歷的結果是 533,則不能確定后一個 3 應該是前一個 3 的左孩子還是右孩子。
不過,即使在兩側子樹都允許有與根相等的元素的情況下,重建出的樹也有可能是唯一的。比如,如果先序遍歷是 2132,那么后一個 2 只能作為 3 的左孩子,而不能作為 1 的右孩子(下圖左);再如,如果層序遍歷是 232,那么后一個 2 只能作為 3 的左孩子,而不能作為 前一個 2 的左孩子(下圖右)。這是因為,按照先序或層序的限制,在安放后一個 2 時,有些「生長點」已經不可用了。但這種情況只能在算法執行過程中發現,無法事先判斷,也就是說,很難給出在「兩側子樹都允許有與根相等的元素」的前提下樹能夠唯一重建的充要條件。
再看 b2,即已知中序遍歷的情況。我們發現,如果允許有重復元素,那么也無法在事先判斷樹是否唯一。例如,當先序遍歷為 1213,中序遍歷為 1231 時,重建出的樹有兩種可能:若認為中序遍歷中的前一個 1 為根,則它有一棵右子樹,其先序遍歷為 213,中序遍歷為 231(下圖中);若認為中序遍歷中的后一個 1 為根,則它有一棵左子樹,其先序遍歷為 213,中序遍歷為 123(下圖左)。但在先序遍歷仍為 1213,把中序遍歷改為 1321 時,重建出的樹就是唯一的了(下圖右):此時只能認為中序遍歷中的后一個 1 為根。否則,我們將需要重建一棵先序遍歷為 213、中序遍歷為 321 的子樹,而這樣的樹是不存在的。
「先序遍歷為 213、中序遍歷為 321 的樹不存在」—— 這個事實其實頗有深意。它告訴我們,并不是把先序遍歷隨便排列一下作為中序遍歷,都存在對應的樹。事實上,3 個元素的全排列有 3! = 6 種,而 3 個結點組成的二叉樹只有 Catalan(3) = 5 種,差的那一種恰好就是本段的例子。
如果已知的是層序和中序遍歷,同樣無法事先判斷樹是否唯一。例如,固定中序遍歷為 121,若層序遍歷也是 121,則樹有下圖左、中兩種可能;但若層序遍歷是 211,則樹就只有下圖右一種可能了。于是,與 BST 的情況類似,若在已知中序遍歷的情況下允許樹中有重復元素,則很難給出樹能夠唯一的充要條件。
作為總結,第二節給出的充分條件中,b2、b3 兩條中的「不含重復元素」,基本可以認為是必要的。除了對于 BST 可以放寬為「允許一側子樹有與根相等的元素」以外,其它情形很難放寬。
總結
以上是生活随笔為你收集整理的c语言判断一个已知的二叉树是否是二叉排序树_10584 二叉树怎样序列化才能重建...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python常见内置函数_python常
- 下一篇: postgresql 编码_上万份编码测