成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

學(xué)習(xí)JavaScript數(shù)據(jù)結(jié)構(gòu)與算法 — AVL樹

impig33 / 3453人閱讀

摘要:可以看到,一次左單旋將右側(cè)子樹的高度減小了,而左側(cè)子樹的高度增加了。如圖,對(duì)進(jìn)行右單旋,需要左子樹的右子樹的高度小于等于左子樹的高度,否則不能達(dá)到平衡的效果,只是把不平衡性從左邊轉(zhuǎn)移到了右邊。

AVL樹

普通二叉搜索樹可能出現(xiàn)一條分支有多層,而其他分支卻只有幾層的情況,如圖1所示,這會(huì)導(dǎo)致添加、移除和搜索樹具有性能問題。因此提出了自平衡二叉樹的概念,AVL樹(阿德爾森-維爾斯和蘭迪斯樹)是自平衡二叉樹的一種,AVL樹的任一子節(jié)點(diǎn)的左右兩側(cè)子樹的高度之差不超過1,所以它也被稱為高度平衡樹。

圖1

要將不平衡的二叉搜索樹轉(zhuǎn)換為平衡的AVL樹需要對(duì)樹進(jìn)行一次或多次旋轉(zhuǎn),旋轉(zhuǎn)方式分為左單旋、右單旋、左-右雙旋、右-左雙旋。

左單旋

對(duì)某一節(jié)點(diǎn)B(圖2)做左單旋,處理過程相當(dāng)于,斷開B與父節(jié)點(diǎn)A的連接,將B的右子節(jié)點(diǎn)D與A連接,將B作為D的左子節(jié)點(diǎn),將D的左子節(jié)點(diǎn)E作為B的右子節(jié)點(diǎn)。以圖1的二叉樹為例,對(duì)鍵值為15的節(jié)點(diǎn)做左單旋,首先斷開15與11的連接,再將20與11連接,將15作為20的左子節(jié)點(diǎn),最后將18作為15的右子節(jié)點(diǎn);可以想象為以15為中心做了一定的逆時(shí)針旋轉(zhuǎn)。結(jié)果如圖3。

圖2

圖3

再看圖2,根據(jù)搜索二叉樹的性質(zhì),肯定有D>B>A,E>B,因此旋轉(zhuǎn)過后,能夠保證 右子節(jié)點(diǎn) > 父節(jié)點(diǎn) > 左子節(jié)點(diǎn),不會(huì)破壞樹的結(jié)構(gòu)。
可以看到,一次左單旋將右側(cè)子樹的高度減小了1,而左側(cè)子樹的高度增加了1。實(shí)現(xiàn)代碼如下:

function roateLeft(AvlNode) {
        var node = AvlNode.right; // 保存右子節(jié)點(diǎn)
        AvlNode.right = node.left; // node的左子節(jié)點(diǎn)連接到AvlNode成為其右子節(jié)點(diǎn)
        node.left = AvlNode; // AvlNode連接到node成為其左子節(jié)點(diǎn)
        return node; // 返回node,連接到AvlNode最初的父節(jié)點(diǎn)
}
右單旋

右單旋與左單選類似,以某一節(jié)點(diǎn)B(圖4)做右單旋,首先斷開B與其父節(jié)點(diǎn)A的連接,將B的左子節(jié)點(diǎn)C與A連接,將C的右子節(jié)點(diǎn)F作為B的左子節(jié)點(diǎn)。同樣的,因?yàn)橛蠧>A,B>F>C,因此旋轉(zhuǎn)過后,不會(huì)破壞樹的結(jié)構(gòu)。可以看到,一次右單旋使節(jié)點(diǎn)的左側(cè)子樹高度減小了1,而右側(cè)子樹的高度增加了1。

圖4

實(shí)現(xiàn)代碼如下:

function roateRight(AvlNode) {
        var node = AvlNode.left; // 保存左子節(jié)點(diǎn)
        AvlNode.left = node.right; // 將node的右子節(jié)點(diǎn)連接到AvlNode成為其左子節(jié)點(diǎn)
        node.right = AvlNode; // AvlNode連接到node,成為其右子節(jié)點(diǎn)
        return node; // 返回node連接到AvlNode最初的父節(jié)點(diǎn)
}
左-右雙旋

左單旋、右單旋在某些情況下是不能達(dá)到平衡樹的目的的。如圖4,對(duì)B進(jìn)行右單旋,需要左子樹C的右子樹F的高度小于等于左子樹E的高度,否則不能達(dá)到平衡的效果,只是把不平衡性從左邊轉(zhuǎn)移到了右邊。圖5演示了這種情況。同樣的,左單旋也有這個(gè)問題。

圖5

因此為了達(dá)到目的,需要先對(duì)旋轉(zhuǎn)節(jié)點(diǎn)的左子節(jié)點(diǎn)做左單旋,再對(duì)旋轉(zhuǎn)節(jié)點(diǎn)做右單旋。如圖6所示,先對(duì)節(jié)點(diǎn)B的左子節(jié)點(diǎn)C做左單旋,可以看到,這個(gè)操作,相當(dāng)于將節(jié)點(diǎn)C的不平衡性從右側(cè)轉(zhuǎn)移到了左側(cè),從而滿足了上述右單旋的條件;最后再對(duì)B節(jié)點(diǎn)做右單旋操作,最終達(dá)到了平衡的目的。

圖6

實(shí)現(xiàn)代碼如下:

function roateLeftRight(AvlNode) {
        AvlNode.right = roateLeft(AvlNode.right); // 對(duì)右子節(jié)點(diǎn)做左單旋
        return roateRight(AvlNode); // 做右單旋
}
右-左雙旋

同理,如圖2,對(duì)B進(jìn)行左單旋時(shí),需要右子樹D的右子樹F的高度大于等于左子樹E的高度,否則需要進(jìn)行雙旋;即先對(duì)B的右子節(jié)點(diǎn)D做右單旋,再對(duì)B做左單旋。實(shí)現(xiàn)代碼如下:

function roateRightLeft(AvlNode) {
        AvlNode.left = roateRight(AvlNode.left); // 對(duì)左子節(jié)點(diǎn)做右單旋
        return roateLeft(AvlNode); // 做左單旋
}
實(shí)現(xiàn)樹的平衡

首先實(shí)現(xiàn)獲取樹高度的函數(shù):

function getAvlTreeHeight(node) {
        if (node == null) {
            // node不存在返回0
            return 0;
        } else {
            var leftHeight = getAvlTreeHeight(node.left);
            var rightHeight = getAvlTreeHeight(node.right);
            // 返回左子樹、右子樹中的最大高度
            return (leftHeight > rightHeight ? leftHeight : rightHeight) + 1;
        }
}

實(shí)現(xiàn)平衡樹的函數(shù):

function balance(node) {
    if (node == null) {
        return node;
    }
    // 左子樹高度比右子樹高度大1以上
    if (getAvlTreeHeight(node.left) - getAvlTreeHeight(node.right) > 1) {
        if (getAvlTreeHeight(node.left.left) >= getAvlTreeHeight(node.left.right)) {
            // 如果左子樹的左子樹高度大于等于左子樹的右子樹高度
            // 直接進(jìn)行右單旋
            node = roateRight(node);
        } else {
            // 否則需要右-左雙旋
            node = roateRightLeft(node);
        }
        // 右子樹高度比左子樹高度大1以上
    } else if (getAvlTreeHeight(node.right) - getAvlTreeHeight(node.left) > 1) {
        if (getAvlTreeHeight(node.right.right) >= getAvlTreeHeight(node.right.left)) {
            // 如果右子樹的右子樹高度大于等于右子樹的左子樹高度
            // 直接進(jìn)行左單旋
            node = roateLeft(node);
        } else {
            // 否則需要左-右雙旋
            node = roateLeftRight(node);
        }
    }
    return node;
}

在二叉搜索樹的基礎(chǔ)上,每次插入節(jié)點(diǎn),都需要做一次樹的平衡處理:

var insertNode = function(node, newNode){
    if (newNode.key < node.key){
        if (node.left === null){
            node.left = newNode;
            // 插入節(jié)點(diǎn)后,做樹的平衡處理
            node.left = balance(node.left);
        } else {
            insertNode(node.left, newNode);
        }
    } else {
        if (node.right === null){
            node.right = newNode;
            // 插入節(jié)點(diǎn)后,做樹的平衡處理
            node.right = balance(node.right);
        } else {
            insertNode(node.right, newNode);
        }
    }
}

綜上,一顆自平衡AVL樹的原理及實(shí)現(xiàn)就完成了。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/86807.html

相關(guān)文章

  • JavaScript數(shù)據(jù)結(jié)構(gòu)算法(十)自平衡

    摘要:為了解決這類問題,我們進(jìn)行自平衡樹的學(xué)習(xí)。自平衡樹常見有兩種樹和紅黑樹。自平衡樹準(zhǔn)備知識(shí)節(jié)點(diǎn)的高度和平衡因子節(jié)點(diǎn)高度從節(jié)點(diǎn)到任意子節(jié)點(diǎn)的彼岸的最大值。 前面介紹了二叉樹和二叉樹搜索樹的創(chuàng)建和使用,接下來我們繼續(xù)學(xué)習(xí)關(guān)于樹的更多知識(shí)。BST存在一個(gè)問題,就是當(dāng)我們多次添加節(jié)點(diǎn)數(shù),有可能造成一種情況,樹的一條邊可能會(huì)非常深,有非常多的層,而另一條分支卻只有幾層。當(dāng)我們需要進(jìn)行添加、移除和搜...

    msup 評(píng)論0 收藏0
  • 學(xué)習(xí)javascript數(shù)據(jù)結(jié)構(gòu)(四)——

    摘要:原文博客地址學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)四樹知乎專欄簡(jiǎn)書專題前端進(jìn)擊者知乎前端進(jìn)擊者簡(jiǎn)書博主博客地址的個(gè)人博客人之所能,不能兼?zhèn)?,棄其所短,取其所長(zhǎng)。通常子樹被稱作左子樹和右子樹。敬請(qǐng)期待數(shù)據(jù)結(jié)構(gòu)篇最后一篇文章學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)五圖參考文章樹數(shù)據(jù)結(jié)構(gòu)二叉樹 前言 總括: 本文講解了數(shù)據(jù)結(jié)構(gòu)中的[樹]的概念,盡可能通俗易懂的解釋樹這種數(shù)據(jù)結(jié)構(gòu)的概念,使用javascript實(shí)現(xiàn)了樹,如有紕漏,歡迎批評(píng)指正。 ...

    Dean 評(píng)論0 收藏0
  • 一文掌握關(guān)于Java數(shù)據(jù)結(jié)構(gòu)所有知識(shí)點(diǎn)(歡迎一起完善)

    摘要:是棧,它繼承于。滿二叉樹除了葉結(jié)點(diǎn)外每一個(gè)結(jié)點(diǎn)都有左右子葉且葉子結(jié)點(diǎn)都處在最底層的二叉樹。沒有鍵值相等的節(jié)點(diǎn)。這是數(shù)據(jù)庫(kù)選用樹的最主要原因。 在我們學(xué)習(xí)Java的時(shí)候,很多人會(huì)面臨我不知道繼續(xù)學(xué)什么或者面試會(huì)問什么的尷尬情況(我本人之前就很迷茫)。所以,我決定通過這個(gè)開源平臺(tái)來幫助一些有需要的人,通過下面的內(nèi)容,你會(huì)掌握系統(tǒng)的Java學(xué)習(xí)以及面試的相關(guān)知識(shí)。本來是想通過Gitbook的...

    keithxiaoy 評(píng)論0 收藏0
  • 二叉排序

    摘要:節(jié)點(diǎn)的構(gòu)造函數(shù)默認(rèn)為其初始化都是。二叉排序樹插入插入節(jié)點(diǎn)只要遵循一個(gè)原則就好大與就向中插入,小于就向插入。初始化數(shù)據(jù)樹現(xiàn)在來試試實(shí)例化一個(gè)來看看效果。 JavaScript 數(shù)據(jù)結(jié)構(gòu)篇 之 BST 前言 有段時(shí)間沒有寫文章了,整個(gè)人感覺變得有點(diǎn)懶散了,大家可不要向我一樣哦~今天開始 seaconch 打算開始記錄 JavaScript 數(shù)據(jù)結(jié)構(gòu)的學(xué)習(xí)經(jīng)歷。至此,開始。 課本:《學(xué)習(xí)J...

    Soarkey 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<