[從頭學數學] 第243節 關於平衡二叉樹的Python實現
阿新 • • 發佈:2018-12-25
劇情提要:
阿偉看到了一本比較有趣的書,是關於《計算幾何》的,2008年由北清派出版。很好奇
它裡面講了些什麼,就來看看啦。
正劇開始:
星曆2016年07月23日 16:30:59, 銀河系厄爾斯星球中華帝國江南行省。
據說這種樹有退化的問題,只寫了這一個函式,這個問題就暴露了。
左旋,右旋,左平衡,右平衡都沒有問題。
阿偉看到了一本比較有趣的書,是關於《計算幾何》的,2008年由北清派出版。很好奇
它裡面講了些什麼,就來看看啦。
正劇開始:
星曆2016年07月23日 16:30:59, 銀河系厄爾斯星球中華帝國江南行省。
[工程師阿偉]正在和[機器小偉]一起研究[計算幾何]]。
關於平衡二叉樹ALV,一直沒有找到Python版本,當然C++和Java版本有很多。
於是想自己實現一個。
首先研究了下二叉查詢樹:
<span style="font-size:18px;">#用列表模擬平衡二叉樹 class BBT(): #對於按<法則排序好的列表,檢視其中任意元素的左右葉子,返回序號 #二叉查詢樹,會有退化現象 def childindex(self, total, cur, mode = 'L'): #total: 列表總元素數,位序取值範圍 0 ~ total-1 #cur: 當前元素在列表中的位序 start = 0; end = total-1; if (cur == start or cur == end): return [cur, -1, -1, math.ceil(math.log(total)/math.log(2))-1]; midArray = []; mid = (start + end+1)//2; left = (start + mid+1)//2; right = (mid + end+1)//2; level = 0; midArray.append(mid); while (mid != cur and level < 10): if (mid < cur): start = mid; mid_ = (end + mid+1)//2; right = (end + mid_+1)//2; left = (mid + mid_+1)//2; mid = mid_; elif (mid > cur): end = mid; mid_ = (start + mid+1)//2; right = (mid+mid_+1)//2; left = (start+mid_+1)//2; mid = mid_; midArray.append(mid); level+=1; if (left in midArray): left = -1; if (right in midArray): right = -1; return [cur, left, right, level]; </span>
據說這種樹有退化的問題,只寫了這一個函式,這個問題就暴露了。
結論就是這種樹是沒用的,也沒有研究下去的價值了。
接著看ALV樹。
<span style="font-size:18px;">### # @usage 平衡二叉樹的Python實現,C++/Java實現到處都有 # @author mw # @date 2016年07月23日 星期六 10:09:31 # @param # @return # ### #AVL樹 class ALVTreeNode(): def __init__(self, data, height, freq, left, right): self.data = data; self.height = height; self.freq = freq; self.left = left; self.right = right; #小於<法則 def less(x1, x2): return x1 < x2; class ALVTree(): def __init__(self): self.root = None; self.left = None; self.right = None; def setValue(self, root, left, right): self.root = root; self.left = left; self.right = right; def getValue(self): return [self.root, self.left, self.right]; def info(self, node): if (node == None): info = 'None'; else: if (node.left == None): sL = 'None'; else: sL = node.left.data; if (node.right == None): sR = 'None'; else: sR = node.right.data; info = [node.data, node.height, node.freq, sL, sR]; print(info); def genNode(self, data, height, freq, left, right): return ALVTreeNode(data, height, freq, left, right); def height(self, node): if (node == None): return 0; else: lh = self.height(node.left); rh = self.height(node.right); node.height = max(lh, rh)+1; return node.height; #左左情況下的旋轉 #輸入的是高度最高的節點,也就是待處理的root def rotateLL(self, k2): k1 = k2.left; k2.left = k1.right; k1.right = k2; k2.height = max(self.height(k2.left), self.height(k2.right))+1; k1.height = max(self.height(k1.left), k2.height)+1; return k1; #右右情況下的旋轉 #輸入的是高度最高的節點,也就是待處理的root def rotateRR(self, k2): k1 = k2.right; k2.right = k1.left; k1.left = k2; k2.height = max(self.height(k2.left), self.height(k2.right))+1; k1.height = max(self.height(k1.right), k2.height)+1; return k1; #左右情況的旋轉 #輸入的是高度最高的節點,也就是待處理的root def rotateLR(self, k3): k1 = self.rotateRR(k3.left); k3.left = k1; k2 = self.rotateLL(k3); return k2; #右左情況的旋轉 #輸入的是高度最高的節點,也就是待處理的root def rotateRL(self, k3): k1 = self.rotateLL(k3.right); k3.right = k1; k2 = self.rotateRR(k3); return k2; #平衡值 def balanceFlag(self, node): return self.height(node.left)-self.height(node.right); #左平衡 def LBalance(self, root): p = root; c = p.left; rc = c.right; cbf = self.balanceFlag(c); if (cbf == 1): root = self.rotateLL(root); elif (cbf == -1): root = self.rotateLR(root); return root; #右平衡 def RBalance(self, root): p = root; c = p.right; lc = c.left; cbf = self.balanceFlag(c); if (cbf == -1): root = self.rotateRR(root); elif (cbf == 1): root = self.rotateRL(root); return root; </span>
左旋,右旋,左平衡,右平衡都沒有問題。
然後到插入就卡殼了,怎麼都調不出結果來。
下面這是廢碼,留著看看吧:
<span style="font-size:18px;"> #插入 def insert_x(self, node, data): if (node == None): node = self.genNode(data, 1, 0, None, None); return node; else: if (less(data, node.data)): if (node.left == None): node.left = self.genNode(data, 1, 0, None, None); self.height(node); if (2 == self.height(node.left) - self.height(node.right)): if less(data, node.left.data): self.rotateLL(node); else: self.rotateLR(node); return node; else: return self.insert(node.left, data); elif (less(node.data, data)): if (node.right == None): node.right = self.genNode(data, 1, 0, None, None); self.height(node); if (2 == self.height(node.right) - self.height(node.left)): if less(node.right.data, data): self.rotateRR(node); else: self.rotateRL(node); return node; else: return self.insert(node.right, data); else: #如果相等,就把頻率加1 node.freq+=1; #插入 def insert(self, root, data, tall = 1): if (root == None): tall = 1; root = self.genNode(data, 1, 0, None, None); self.setValue(root, root.left, root.right); return 1; elif (root.data == data): #如果相等,就把頻率加1 root.freq+=1; self.setValue(root, root.left, root.right); return 0; elif (less(data, root.data)): if (root.left == None): self.setValue(root, self.genNode(data, 1, 0, None, None), root.right); return 1; else: self.insert(root.left, data, tall): if (tall): root = self.root; rbf = self.balanceFlag(root); if (rbf == 1): root = LBalance(root); tall = 0; elif (rbf == 0): tall = 1; elif (rbf == -1): tall = 0; self.root = root; return 0; elif (less(root.data, data)): if (self.insert(root.right, data, tall)): return 1; if (tall): rbf = self.balanceFlag(root); if (rbf == -1): root = RBalance(root); tall = 0; elif (rbf == 0): tall = 1; elif (rbf == 1): tall = 0; self.root = root; return 0; if __name__ == '__main__': alv = ALVTree(); alv.insert(None, 0); root = alv.root; alv.info(root); for i in range(3): alv.insert(root, i); root = alv.root; alv.info(root);</span>
阿偉最後終於發現了問題所在,在C++中可以用*號給每個節點作為儲存地址,
在Java裡面也可以用&,雖然效果差了點,但在Python裡面,啥都沒有。
插入函式中需要不斷地new出新地址來儲存節點,但這確實是做不到的。
當然,只要解決了每個節點的儲存空間問題,用Python也是可以實現這個ALV樹的。
用連結串列來存也不是不可以,但那樣效率肯定會非常差。
那是不是可以這麼說: Python可以和資料結構說拜拜了呢。
本節到此結束,欲知後事如何,請看下回分解。