摘要:列表的第二個元素的本身是一個表示左子樹的列表。子樹具有根節(jié)點和兩個表示葉節(jié)點的空列表。重要的是要記住這種表示的是左右子樹引用的是其他二叉樹的實例。為了完成一個簡單的二叉樹數(shù)據(jù)結(jié)構(gòu)的定義,我們寫出訪問參見左右子節(jié)點和根值的方法。
“嵌套列表”表示樹
在用嵌套列表表示樹時,我們使用 Python 的列表來編寫這些函數(shù)。雖然把界面寫成列表的一系列方法與我們已實現(xiàn)其他的抽象數(shù)據(jù)類型有些不同,但這樣做比較有意思,因為它為我們提供一個簡單、可以直接查看的遞歸數(shù)據(jù)結(jié)構(gòu)。在列表實現(xiàn)樹時,我們將存儲根節(jié)點作為列表的第一個元素的值。列表的第二個元素的本身是一個表示左子樹的列表。這個列表的第三個元素表示在右子樹的另一個列表。為了說明這個存儲結(jié)構(gòu),讓我們來看一個例子。圖 1 展示出一個簡單的樹以及相應(yīng)的列表實現(xiàn)。
圖 1: 簡單樹
myTree = ["a", #root ["b", #left subtree ["d" [], []], ["e" [], []] ], ["c", #right subtree ["f" [], []], [] ] ]
請注意,我們可以使用索引來訪問列表的子樹。樹的根是myTree[0],根的左子樹是myTree[1],和右子樹是myTree[2]。下面的代碼說明了如何用列表創(chuàng)建簡單樹。一旦樹被構(gòu)建,我們可以訪問根和左、右子樹。嵌套列表法一個非常好的特性就是子樹的結(jié)構(gòu)與樹相同,本身是遞歸的。子樹具有根節(jié)點和兩個表示葉節(jié)點的空列表。列表的另一個優(yōu)點是它容易擴(kuò)展到多叉樹。在樹不僅僅是一個二叉樹的情況下,另一個子樹只是另一個列表。
myTree = ["a", ["b", ["d",[],[]], ["e",[],[]] ], ["c", ["f",[],[]], []] ] print(myTree) print("left subtree = ", myTree[1]) print("root = ", myTree[0]) print("right subtree = ", myTree[2])
讓我們定義一些函數(shù),使我們很容易像使用列表一樣操作樹。請注意,我們不會去定義一個二叉樹類。我們將編寫的函數(shù)將只是操作列表使之類似于樹。
def BinaryTree(r): return [r, [], []]
該二叉樹只是構(gòu)建一個根節(jié)點和兩個空子節(jié)點的列表。左子樹添加到樹的根,我們需要插入一個新的列表到根列表的第二個位置。我們必須注意,如果列表中已經(jīng)有值在第二個位置,我們需要跟蹤它,將新節(jié)點插入樹中作為其直接的左子節(jié)點。Listing 1 顯示了插入左子節(jié)點。
Listing 1
def insertLeft(root,newBranch): t = root.pop(1) if len(t) > 1: root.insert(1,[newBranch,t,[]]) else: root.insert(1,[newBranch, [], []]) return root
請注意,插入一個左子節(jié)點,我們首先獲取對應(yīng)于當(dāng)前左子節(jié)點的列表(可能是空的)。然后,我們添加新的左子節(jié)點,將原來的左子節(jié)點作為新節(jié)點的左子節(jié)點。這使我們能夠?qū)⑿鹿?jié)點插入到樹中的任何位置。對于insertRight的代碼類似于insertLeft,如Listing 2 中。
Listing 2
def insertRight(root,newBranch): t = root.pop(2) if len(t) > 1: root.insert(2,[newBranch,[],t]) else: root.insert(2,[newBranch,[],[]]) return root
為了完善樹的實現(xiàn)(參見Listing3),讓我們寫幾個用于獲取和設(shè)置根值的函數(shù),以及獲得左邊或右邊子樹的函數(shù)。
Listing 3
def getRootVal(root): return root[0] def setRootVal(root,newVal): root[0] = newVal def getLeftChild(root): return root[1] def getRightChild(root): return root[2]
以下是完整的嵌套列表表示樹的代碼
def BinaryTree(r): return [r, [], []] def insertLeft(root,newBranch): t = root.pop(1) if len(t) > 1: root.insert(1,[newBranch,t,[]]) else: root.insert(1,[newBranch, [], []]) return root def insertRight(root,newBranch): t = root.pop(2) if len(t) > 1: root.insert(2,[newBranch,[],t]) else: root.insert(2,[newBranch,[],[]]) return root def getRootVal(root): return root[0] def setRootVal(root,newVal): root[0] = newVal def getLeftChild(root): return root[1] def getRightChild(root): return root[2] r = BinaryTree(3) insertLeft(r,4) insertLeft(r,5) insertRight(r,6) insertRight(r,7) l = getLeftChild(r) print(l) setRootVal(l,9) print(r) insertLeft(l,11) print(r) print(getRightChild(getRightChild(r)))節(jié)點和引用
我們第二種表示樹的方式——節(jié)點和引用。在這種情況下,我們將定義具有根,以及左右子樹屬性的類。由于這種表示更緊密地結(jié)合了面向?qū)ο蟮姆绞?,我們將繼續(xù)使用這種表示完成本章的其余部分。
使用節(jié)點和引用,我們認(rèn)為該樹的結(jié)構(gòu)類似于圖 2 所示。
圖 2:使用節(jié)點和引用表示簡單樹
我們將開始用簡單的節(jié)點和引用的類定義如Listing 4 所示。重要的是要記住這種表示的是左右子樹引用的是其他二叉樹的實例。例如,當(dāng)我們插入一個新的左子節(jié)點到樹上時,我們創(chuàng)建了二叉樹的另一個實例,修改了根節(jié)點的self leftChild使之指向新的樹。
Listing 4
class BinaryTree: def __init__(self,rootObj): self.key = rootObj self.leftChild = None self.rightChild = None
注意Listing 4 中,構(gòu)造函數(shù)需要得到一些類型的對象存儲在根中。就像你可以在列表中存儲你喜歡的任何一種類型,樹的根對象可以指向任何一種類型。對于我們之前的例子中,我們將存儲節(jié)點設(shè)為根值的名稱。使用節(jié)點和引用來表示圖 2 中的樹,我們將創(chuàng)建二叉樹類的 6 個實例。
接下來讓我們看一下我們需要構(gòu)建的根節(jié)點以外的函數(shù)。為了添加左子節(jié)點,我們將創(chuàng)建一個新的二叉樹,并設(shè)置根的左屬性以指向這個新對象。insertLeft的代碼Listing 5 所示。
Listing 5
def insertLeft(self,newNode): if self.leftChild == None: self.leftChild = BinaryTree(newNode) else: t = BinaryTree(newNode) t.leftChild = self.leftChild self.leftChild = t
我們必須考慮兩種情況進(jìn)行插入。第一種情況是,沒有左子節(jié)點。當(dāng)沒有左子節(jié)點時,將新節(jié)點添加即可。第二種情況的特征是,當(dāng)前存在左子節(jié)點。在第二種情況下,我們插入一個節(jié)點并將之前的子節(jié)點降一級。第二種情況是由else語句在Listing 5的第 4 行進(jìn)行處理。
對于insertRight的代碼必須考慮一個對稱的情況。要么沒有右子節(jié)點,要么我們必須插入根和現(xiàn)有的右子節(jié)點之間。插入代碼Listing 6 所示。
Listing 6
def insertRight(self,newNode): if self.rightChild == None: self.rightChild = BinaryTree(newNode) else: t = BinaryTree(newNode) t.rightChild = self.rightChild self.rightChild = t
為了完成一個簡單的二叉樹數(shù)據(jù)結(jié)構(gòu)的定義,我們寫出訪問(參見Listing 7)左右子節(jié)點和根值的方法。
Listing 7
def getRightChild(self): return self.rightChild def getLeftChild(self): return self.leftChild def setRootVal(self,obj): self.key = obj def getRootVal(self): return self.key
既然我們已經(jīng)有了所有創(chuàng)建和操作二叉樹的方法,讓我們再進(jìn)一步檢查它的結(jié)構(gòu)。讓我們把每一個節(jié)點比作一個簡單的樹的根,并添加節(jié)點 B 和 C 作為子節(jié)點。 下面的代碼就是創(chuàng)建樹,并存儲一些鍵值,為左右子節(jié)點賦值。注意,左右子節(jié)點和根都是同一個二叉樹類的不同對象。正如我們之前樹的定義中說的,我們能夠把一個二叉樹的任何子節(jié)點當(dāng)成二叉樹來做處理。
class BinaryTree: def __init__(self,rootObj): self.key = rootObj self.leftChild = None self.rightChild = None def insertLeft(self,newNode): if self.leftChild == None: self.leftChild = BinaryTree(newNode) else: t = BinaryTree(newNode) t.leftChild = self.leftChild self.leftChild = t def insertRight(self,newNode): if self.rightChild == None: self.rightChild = BinaryTree(newNode) else: t = BinaryTree(newNode) t.rightChild = self.rightChild self.rightChild = t def getRightChild(self): return self.rightChild def getLeftChild(self): return self.leftChild def setRootVal(self,obj): self.key = obj def getRootVal(self): return self.key r = BinaryTree("a") print(r.getRootVal()) print(r.getLeftChild()) r.insertLeft("b") print(r.getLeftChild()) print(r.getLeftChild().getRootVal()) r.insertRight("c") print(r.getRightChild()) print(r.getRightChild().getRootVal()) r.getRightChild().setRootVal("hello") print(r.getRightChild().getRootVal())
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/44196.html
摘要:圖展示了二叉搜索樹的這一特性,顯示的鍵沒有關(guān)聯(lián)任何的值。因為我們必須能夠創(chuàng)建和使用一個空的二叉搜索樹,所以我們將使用兩個類來實現(xiàn),第一個類我們稱之為,第二個類我們稱之為。圖說明了將新節(jié)點插入到一個二叉搜索樹的過程。 二叉搜索樹 我們已經(jīng)知道了在一個集合中獲取鍵值對的兩種不同的方法?;貞浺幌逻@些集合是如何實現(xiàn)ADT(抽象數(shù)據(jù)類型)MAP的。我們討論兩種ADT MAP的實現(xiàn)方式,基于列表的...
摘要:我們知道,當(dāng)樹變得不平衡時和操作會使二叉搜索樹的性能降低到。樹實現(xiàn)抽象數(shù)據(jù)類型就像一個普通的二叉搜索樹,唯一不同的是這棵樹的工作方式。我們通過比較每個節(jié)點的左右子樹的高度完成比較。 平衡二叉搜索樹 在上一節(jié)中我們討論了建立一個二叉搜索樹。我們知道,當(dāng)樹變得不平衡時get和put操作會使二叉搜索樹的性能降低到O(n)。在這一節(jié)中我們將看到一種特殊的二叉搜索樹,它可以自動進(jìn)行調(diào)整,以確保樹...
摘要:左子樹的加法運算結(jié)果為,右子樹的減法運算結(jié)果為。如圖,該圖說明了隨著每個新的字符被讀入后該解析樹的內(nèi)容和結(jié)構(gòu)。使函數(shù)走向基點的遞歸過程就是調(diào)用求值函數(shù)計算當(dāng)前節(jié)點的左子樹右子樹的值。最后,我們將在圖中創(chuàng)建的解析樹上遍歷求值。 解析樹 完成樹的實現(xiàn)之后,現(xiàn)在我們來看一個例子,告訴你怎么樣利用樹去解決一些實際問題。在這個章節(jié),我們來研究解析樹。解析樹常常用于真實世界的結(jié)構(gòu)表示,例如句子或數(shù)...
摘要:一旦子樹平衡因子為零,那么父節(jié)點的平衡因子不會發(fā)生改變。新根的父節(jié)點將成為舊根的父節(jié)點。因為其他操作都是移動整個子樹,被移動的子樹內(nèi)的節(jié)點的平衡因子不受旋轉(zhuǎn)的影響。讓表示以為根節(jié)點的子樹的高度。 既然,我們已經(jīng)證明,保持 AVL 樹的平衡將會使性能得到很大的提升,那我們看看如何在程序中向樹插入一個新的鍵值。因為所有的新鍵是作為葉節(jié)點插入樹的,而新葉子的平衡因子為零,所以我們對新插入的節(jié)...
摘要:遞歸列表可以使用遞歸函數(shù)最為自然地操作,就像它們的名稱和結(jié)構(gòu)表示的那樣。處理遞歸列表遞歸列表結(jié)構(gòu)將列表表示為首個元素和列表的剩余部分的組合。例如,我們可以使用高階遞歸函數(shù)將樹的每個葉子平方,它的結(jié)構(gòu)類似于。成員測試會遞歸遍歷整個列表。 3.3 遞歸數(shù)據(jù)結(jié)構(gòu) 來源:3.3 Recursive Data Structures 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 在第二...
閱讀 3759·2021-10-11 11:09
閱讀 1397·2021-09-24 10:35
閱讀 3492·2021-07-29 13:48
閱讀 534·2019-08-30 13:15
閱讀 2588·2019-08-30 12:53
閱讀 3344·2019-08-30 12:44
閱讀 2771·2019-08-29 16:57
閱讀 1022·2019-08-29 12:26