1. 程式人生 > >[從頭學數學] 第243節 關於平衡二叉樹的Python實現

[從頭學數學] 第243節 關於平衡二叉樹的Python實現

劇情提要:
阿偉看到了一本比較有趣的書,是關於《計算幾何》的,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可以和資料結構說拜拜了呢。

本節到此結束,欲知後事如何,請看下回分解。