記住上一節樹的定義,在定義的基礎上,我們用以下的函數建立并操作二叉樹:
binarytree() 建立一個二叉樹執行個體
getleftchild() 傳回節點的左孩子
getrightchild() 傳回節點的右孩子
setrootval(val) 把val變量值賦給目前節點
getrootval() 傳回目前節點對象。
insertleft(val) 建立一個新二叉樹作為目前節點的左孩子
insertright(val) 建立一個新二叉樹作為目前節點的右孩子。
實作樹的關鍵點是合适的存儲技術。python提供兩種可用方法,是以在做出決定之前,先查驗一下兩種技術。第一種叫“清單的清單”,第二種叫“節點與引用”
在清單的清單方法中,我們使用清單資料結構來實作上面的函數,雖然這種操作方法與我們以前的抽象資料結構很不相同,不過它很有意思的一點是,這是一個簡單的遞歸結構,我們可以直接檢視和檢查。
在清單的清單樹中,根節點的數值是清單的第一個元素,第二個元素是它的左子樹,第三個元素就是它的右子樹。為說明這個存儲結構,我們看一個例子。圖1所示為一個如此實作的簡單樹。
圖1 一棵小樹
有沒有發現我們可以直接用清單的索引來通路子樹。樹根是mytree[0],左子樹是mytree[1],右子樹是mytree[2]。下面的代碼就是用清單建立了一棵樹,完成之後,就可以通路它的根和子樹,它的好處在于清單中的“元素清單”就代表了子樹,與樹有相同的結構,是以它的結構是遞歸的。如果一個子樹有根節點,但是左右子樹都是空清單,那麼它就是葉子。另一個好處是這種方法産生的樹可以推廣到“多叉樹”而不僅是二叉樹,因為另一個子樹也不過是一個清單而已。
現在來為這種樹形資料結構定義一些函數以友善使用。注意我們沒有定義一個二叉樹類,這些函數隻是幫助操作一個清單,即使工作對象是一個樹。
def binarytree(r):
return [r, [], []]
binarytree函數簡單地建立了一個清單,内中隻有一個根節點和兩個空清單作為孩子。要增加左子樹的話,就要插入一個新清單到根清單的第二個位置上。要小心一點,如果第二個位置上已經儲存了資料,需要跟蹤這個資料,并把它下壓一級,作為新插入清單的左孩子。listing1是插入左孩子的代碼。
listing 1
def insertleft(root,newbranch):
t= root.pop(1)
iflen(t)
>1:
root.insert(1,[newbranch,t,[]])
else:
root.insert(1,[newbranch, [], []])
return root
注意,要插入左孩子,先得到原來左孩子位置的清單(可能是空的),當加入新左孩子的時候,把原來的左孩子作為新節點的孩子。這樣,我們可以把新節點放在樹中任何位置。
插入右孩子的代碼也很相似,見下面listing 2.
listing 2
def insertright(root,newbranch):
t= root.pop(2)
root.insert(2,[newbranch,[],t])
root.insert(2,[newbranch,[],[]])
下面listing 3是擷取和指派的函數,針對根節點和左右子樹。
listing 3
defgetrootval(root):
return root[0]
defsetrootval(root,newval):
root[0]
= newval
defgetleftchild(root):
return root[1]
defgetrightchild(root):
return root[2]
下面是完整的函數代碼。
第二種表示方法使用節點和引用。在這種情況下我們定義一個類,它的屬性值包括根,和左右子樹。因為這種方法更加面向對象,以後的章節中,我們都用這種表示方法。
使用節點和引用,我們認為樹形結構類似圖2所示。
開始時如linsting 4所示,隻是一個節點的定義和兩個子樹的引用。重要的是,左右子樹的引用的,也是這個類的執行個體。例如,如果要插入一個新的左子樹,那麼建立一個新的樹對象并且修改根節點的self.leftchild指向新樹
listing4
注意上面代碼中,構造函數需要一個對象來儲存在根節點。既然清單可以儲存任意對象,那麼樹的根可以是任意對象。在前面的例子中,我們在根節點中儲存節點的名字。在圖2中那樣的樹,我們要建立6個binarytree的對象。
下面看另外的函數。要在樹中增加一個左子樹,需要建立一個二叉樹對象,并把這個對象指派給leftchild。下面是實作代碼。
listing5
上面代碼中考慮了兩種情況。第一種是沒有左孩子,隻需把節點加到樹上。第二種是此節點已經有左孩子,這時要把現存的左孩子下移一級作為插入節點的左孩子。第二種情況是在上面代碼中第4行的else語句處理的。
插入右孩子的操作也要對稱地考慮,要麼沒有右孩子,否則把節點插入到根與現存右孩子之間。如下面的listing 6
listing6
同樣的,下面的函數是用來取值和指派的。
listing7
現在我們完成了建立和操作樹所需要的全部程式段,現在用它們來驗證一下他們樹的結構。我們先建立一個簡單的樹包含一個根和兩個節點,b和c。下面的代碼就是建立樹,并為鍵,左孩子和右孩子指派。注意左孩子和右孩子和根都是同一個類binarytree的不同對象,如同前面我們的遞歸定義一樣,這使得我們可以象處理二叉樹一樣處理它的子樹。