JS實(shí)現(xiàn)二叉排序樹(shù)

來(lái)自慕課網(wǎng)免費(fèi)教程:https://www.imooc.com/video/15751
做做筆記。

最近才發(fā)現(xiàn)原來(lái)這是《學(xué)習(xí)JavaScript數(shù)據(jù)結(jié)構(gòu)和算法》上的代碼,這是JavaScript中比較好的一本算法書吧,淺顯易懂炊汹。但是作者的一些書寫習(xí)慣有點(diǎn)想不通趴梢,類中所有的屬性包括方法都作為私有屬性皿曲,而應(yīng)該作為私有屬性的卻又作為局部變量蔬将,形成閉包匕累。后面發(fā)現(xiàn)快速排序也有點(diǎn)變種,跟以前學(xué)過(guò)的不太一樣迂求。
但總之也值得一看吧碾盐。

之前也實(shí)現(xiàn)過(guò)鏈表,現(xiàn)在一回憶都忘了揩局,再找當(dāng)時(shí)學(xué)習(xí)時(shí)的代碼文件毫玖,都不知道去哪里了,看來(lái)記錄總是好的凌盯。

二叉排序樹(shù)付枫,又叫二叉搜索樹(shù),又叫二叉查找樹(shù)驰怎。

其特點(diǎn)就是左子節(jié)點(diǎn)一定小于父節(jié)點(diǎn)阐滩,而右子節(jié)點(diǎn)一定大于父節(jié)點(diǎn)。
如圖就是一個(gè)二叉排序樹(shù):


image.png
(1)創(chuàng)建二叉排序樹(shù)县忌,直接貼代碼了:
    function BinaryTree() {
        var Node = function(key) {    
            this.key = key;
            this.left = null;
            this.right = null;
        };

        var root = null;

        this.getRoot = function() {      // 從root開(kāi)始通過(guò)left和right跟其他節(jié)點(diǎn)連接一起掂榔,得到root就相當(dāng)于得到整顆樹(shù)了
            return root;
        }

        this.insert = function(key) {
            var newNode = new Node(key);

            if (root === null) {
                root = newNode;
            } else {
                insertNode(root, newNode);
            }
        };
        var insertNode = function(node, newNode) {     // 構(gòu)建二叉排序樹(shù)
            if (newNode.key < node.key) {
                if (node.left === null) {
                    node.left = newNode;
                } else {
                    insertNode(node.left, newNode);
                }
            } else {
                if (node.right === null) {
                    node.right = newNode;
                } else {
                    insertNode(node.right, newNode);
                }
            }
        };
    }

    var nodes = [8, 3, 10, 1, 6, 14, 4, 7, 13];
    var binaryTree = new BinaryTree();

    nodes.forEach((key) => {
        binaryTree.insert(key);
    });
    console.log(binaryTree.getRoot());

(2)遍歷

二叉排序樹(shù)的中序遍歷是有序且升序的址儒!

        this.inOrderTraverse = function(callback) {   
            inOrderTraverseNode(root, callback);
        };
        var inOrderTraverseNode = function(node, callback) {   // 中序遍歷
            if (node !== null) {
                inOrderTraverseNode(node.left, callback);   //  遍歷完左子樹(shù)
                callback(node.key);                         // 
                inOrderTraverseNode(node.right, callback);
            }
        };
    var callback = function(key) {
        console.log(key);
    }

    console.log('中序遍歷:');
    binaryTree.inOrderTraverse(callback);
image.png

前序,后序都一樣衅疙,之前自己不知道怎么用代碼寫,敲了一遍發(fā)現(xiàn)挺簡(jiǎn)單的鸳慈,換個(gè)位置而已饱溢。直接貼代碼:

        this.preOrderTraverse = function(callback) {
            preOrderTraverseNode(root, callback)
        };
        this.postOrderTraverse = function(callback) {
            postOrderTraverseNode(root, callback)
        };

        var preOrderTraverseNode = function(node, callback) {
            if (node !== null) {
                callback(node.key);
                preOrderTraverseNode(node.left, callback);
                preOrderTraverseNode(node.right, callback);
            }
        };
        var postOrderTraverseNode = function(node, callback) {
            if (node !== null) {
                postOrderTraverseNode(node.left, callback);
                postOrderTraverseNode(node.right, callback);
                callback(node.key);
            }
        };

前序遍歷對(duì)拷貝一個(gè)二叉樹(shù)來(lái)說(shuō),比新創(chuàng)建一個(gè)二叉樹(shù)的成本要低很多走芋,前序遍歷效率要高很多绩郎。后序遍歷則可用于文件路徑系統(tǒng)中。

輸出一下:

    console.log('前序遍歷:');
    binaryTree.preOrderTraverse(callback);

    console.log('后序遍歷:');
    binaryTree.postOrderTraverse(callback);
image.png
image.png

(3)最大值翁逞、最小值節(jié)點(diǎn):

可以看到二叉排序樹(shù)的最小值肋杖、最大值,就是最左邊挖函、最右邊的節(jié)點(diǎn)状植。

        this.getMin = function() {
            return getMinNode(root);
        };
        this.getMax = function() {
            return getMaxNode(root);
        };

        var getMinNode = function(node) {
            if (node) {
                while (node.left) {
                    node = node.left;
                }

                return node.key;
            }
            return null;
        };

        var getMaxNode = function(node) {
            if (node) {
                while (node.right) {
                    node = node.right;
                }

                return node.key;
            }
            return null;
        };

(4)查找節(jié)點(diǎn)是否存在,返回boolean值

        this.search = function(key) {
            return searchNode(root, key);
        };

        var searchNode = function(node, key) {
            if (node === null) {
                return false;
            }
            if (key < node.key) {
                return searchNode(node.left, key);    // 注意遞歸中要return回來(lái)
            } else if (key > node.key) {
                return searchNode(node.right, key);
            } else {
                return true;
            }
        };

(5)刪除節(jié)點(diǎn)

刪除節(jié)點(diǎn)就有點(diǎn)麻煩了怨喘,可分為三種情況:
①刪除葉子節(jié)點(diǎn):如果是葉子節(jié)點(diǎn)津畸,直接刪除

②刪除有左子樹(shù)或右子樹(shù)其一的節(jié)點(diǎn):如果只有右子樹(shù),直接用右子樹(shù)取代該節(jié)點(diǎn)必怜。如果只有左子樹(shù)肉拓,直接用左子樹(shù)取代該節(jié)點(diǎn)。由于二叉排序樹(shù)的特點(diǎn)梳庆,你可以畫一下(比如刪除10或14)暖途,會(huì)發(fā)現(xiàn)結(jié)果還是符合二叉排序樹(shù)的。

③刪除左右子樹(shù)都有的節(jié)點(diǎn)

        this.remove = function(key) {
            root = removeNode(root, key);
            //removeNode(root, key);
        };

        var removeNode = function(node, key) {   // 最終返回的是root元素
            if (node === null) {
                return null;
            }
            if (key < node.key) {
                node.left = removeNode(node.left, key); // 遞歸的最后一層返回是null膏执,而此時(shí)node即為刪除的節(jié)點(diǎn)的父節(jié)點(diǎn)驻售。遞歸的最前一層返回的是root
                return node;

                // removeNode(node.left, key);
                //return
            } else if (key > node.key) {
                node.right = removeNode(node.right, key);
                //removeNode(node.right, key);
                return node;
                //return
            } else {
                if (node.left === null && node.right === null) { // 如果是葉子節(jié)點(diǎn),直接刪除
                    //console.log(root.left.left)
                    node = null;
                    //root.left.left = null
                    //console.log(node === root.left.left)
                    
                    return node;
                    //return
                }

                if (node.left === null) {  // 如果只有右子樹(shù)胧后,直接用右子樹(shù)取代該節(jié)點(diǎn)
                    node = node.right;
                    return node;
                    //return
                } else if (node.right === null) {
                    node = node.left;
                    return node;
                    //return
                }

                // 當(dāng)有兩個(gè)孩子時(shí)
                var aux = findMinNode(node.right);
                node.key = aux.key;
                node.right = removeNode(node.right, aux.key);
                return node;
            }
        };
        var findMinNode = function(node) {
            if (node) {
                while (node.left) {
                    node = node.left;
                }

                return node;   // 與前面不同的是返回了node而不是其值芋浮。
            }
            return null;
        };

具體記錄一下,對(duì)于刪除左右子樹(shù)都存在的節(jié)點(diǎn)的情況壳快,比如刪除節(jié)點(diǎn)3纸巷。

其操作是:找到右子樹(shù)所有節(jié)點(diǎn)中的最小節(jié)點(diǎn)(即4),然后將3賦值為該節(jié)點(diǎn)值:


image.png

然后再把最小節(jié)點(diǎn)刪除眶痰,結(jié)果如下:


image.png

這樣操作后就刪除掉節(jié)點(diǎn)3了瘤旨,且還是保持為二叉排序樹(shù)。

看了這門課還是學(xué)到不少東西的竖伯,這對(duì)于刷編程題會(huì)很有用存哲,后面練習(xí)還沒(méi)看完因宇。看完再記錄一下祟偷。

另外察滑,對(duì)于對(duì)于刪除節(jié)點(diǎn)時(shí),我開(kāi)始想:不是對(duì)樹(shù)的操作嗎修肠,為什么還要把節(jié)點(diǎn)一層一層的返回贺辰,直接操作完return結(jié)束不就好了?(可以看到我注釋掉的代碼)嵌施。

        node = null;                        // 刪除后不return node會(huì)失敗 
        //root.left.left = null             // 刪除成功
        //console.log(node === root.left.left)     // true

可是我改完后發(fā)現(xiàn)不成功饲化。才發(fā)現(xiàn)自己的理解還有待加深,趁這個(gè)機(jī)會(huì)再好好總結(jié)了一下關(guān)于JS的引用:http://www.reibang.com/p/bf043921ff58

另外吗伤,必須得刷一下題目才能鞏固:
中序遍歷
前序遍歷
后序遍歷

二叉搜索樹(shù)結(jié)點(diǎn)最小距離
翻轉(zhuǎn)二叉樹(shù)
二叉樹(shù)的最小深度
二叉樹(shù)的最大深度

加一個(gè)層次遍歷:

        this.levelOrder = function(callback) {
            var arr = [];
            levelOrderNode(root, callback, arr);        
        }

        var levelOrderNode = function(node, callback, arr) {
            
            if (node === null) {
                return;
            }
            arr.push(node);
            
            while (arr.length > 0) {
                var node = arr.shift();
                callback(node.key);
                if (node.left) {
                    arr.push(node.left);
                }
                if (node.right) {
                    arr.push(node.right);
                }
            }

        }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末吃靠,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子足淆,更是在濱河造成了極大的恐慌巢块,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件巧号,死亡現(xiàn)場(chǎng)離奇詭異夕冲,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)裂逐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門歹鱼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人卜高,你說(shuō)我怎么就攤上這事弥姻。” “怎么了掺涛?”我有些...
    開(kāi)封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵庭敦,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我薪缆,道長(zhǎng)秧廉,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任拣帽,我火速辦了婚禮疼电,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘减拭。我一直安慰自己蔽豺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布拧粪。 她就那樣靜靜地躺著修陡,像睡著了一般沧侥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上魄鸦,一...
    開(kāi)封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天宴杀,我揣著相機(jī)與錄音,去河邊找鬼拾因。 笑死婴氮,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的盾致。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼荣暮,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼庭惜!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起穗酥,我...
    開(kāi)封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤护赊,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后砾跃,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體骏啰,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年抽高,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了判耕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡翘骂,死狀恐怖壁熄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情碳竟,我是刑警寧澤草丧,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站莹桅,受9級(jí)特大地震影響昌执,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜诈泼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一懂拾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧铐达,春花似錦委粉、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)汁汗。三九已至,卻和暖如春栗涂,著一層夾襖步出監(jiān)牢的瞬間知牌,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工斤程, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留角寸,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓忿墅,卻偏偏與公主長(zhǎng)得像扁藕,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子疚脐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容