<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          JavaScript實(shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu)中的二叉搜索樹

          共 5055字,需瀏覽 11分鐘

           ·

          2021-01-17 20:40


          二叉樹中的節(jié)點(diǎn)最多只能有兩個(gè)子節(jié)點(diǎn):左側(cè)節(jié)點(diǎn) and 右側(cè)節(jié)點(diǎn),這樣定義有助于我們寫出更高效的在樹中插入,查找,刪除節(jié)點(diǎn)的算法。

          二叉搜索樹(BST)是二叉樹的一種,但是 只允許你在左側(cè)節(jié)點(diǎn)存儲比`父節(jié)點(diǎn)`小的值 , 右側(cè)節(jié)點(diǎn)存儲比`父節(jié)點(diǎn)`大的值 ;如下圖:

          重點(diǎn)

          我在寫二叉樹的時(shí)候有個(gè)疑惑,他是如果插入的呢?為什么有的在坐標(biāo)有的在右邊。

          答:其實(shí)可以看上圖圖上面那一樣,

          • 先插入11,

          • 第一次插入7,由于小于11放到左邊

          • 第二次插入5,比11小,又比7小,然后放到7的左側(cè)

          • 第三次插入15,比11大放到右側(cè)

          • 依次類推

          我 想如果你看到這里,結(jié)合上面 左側(cè)小于父節(jié)點(diǎn),右側(cè)大于父節(jié)點(diǎn) 這句話。一定知道怎么放了;??

          樹的遍歷

          遍歷一棵樹??直指范圍書上的每個(gè)節(jié)點(diǎn)并對他們進(jìn)行某種操作的過程。訪問樹的所有節(jié)點(diǎn)有三種方式:中序,先序。

          中序遍歷

          中序遍歷是一種已上行順序訪問BST所有節(jié)點(diǎn)的遍歷方式,也就是從最小到最大的順序訪問所有節(jié)點(diǎn)。是一種對樹進(jìn)行排序操作

              //  中序遍歷    inOrderTraverse(callback){        this.inOrderTraverseNode(this.root,callback);    }    inOrderTraverseNode(node,callback){        //  停止遞歸的條件        if(node !== null){            //  通過棧先把所有小的都放到棧里面            //  先遍歷左測 小節(jié)點(diǎn)             this.inOrderTraverseNode(node.left,callback);            callback(node.key);            //  后遍歷右測 大節(jié)點(diǎn)            this.inOrderTraverseNode(node.right,callback)        }    }    const b = new BinarySearchTree();    b.inOrderTraverse((value) => console.log(value))    //3  5 6 7 8 9 9 10 ...

          中序 尋找規(guī)則圖示

          先序遍歷

          先序遍歷是以優(yōu)先于后代節(jié)點(diǎn)的順序訪問每個(gè)節(jié)點(diǎn)的,先序遍歷的一種應(yīng)用是打印一個(gè)結(jié)構(gòu)化的文檔。

          先序遍歷會訪問節(jié)點(diǎn)本身,然后在訪問他的左側(cè)子節(jié)點(diǎn),最后反問右側(cè)子節(jié)點(diǎn)。

              //  先序遍歷    perOrderTraverse(callback){        this.perOrderTraverseNode(this.root,callback);    }    perOrderTraverseNode(node,callback){        if(node !== null){            callback(node.key);            this.perOrderTraverseNode(node.left,callback);            this.perOrderTraverseNode(node.right,callback);        }    }    b.perOrderTraverse((value) => console.log(value))   //  11 7 5 3 6 9 8 10 15 13 ...
          先序 尋找規(guī)則圖示

          后序排序

          后續(xù)遍歷是先訪問節(jié)點(diǎn)的后代節(jié)點(diǎn),再訪問節(jié)點(diǎn)本身。后續(xù)遍歷的一種應(yīng)用是計(jì)算一個(gè)目錄及其子目錄中所有文件所占空間的大小;

           
             //  后續(xù)排序    postOrderTraverse(callback){        this.postOrderTraverseNode(this.root,callback);    }    postOrderTraverseNode(node,callback){        if(node !== null){            this.postOrderTraverseNode(node.left,callback)            this.postOrderTraverseNode(node.right,callback)            callback(node.key)        }    }    b.postOrderTraverse((value) => console.log(value))   //  3 6 5 8 10 9 7 12 14 ...
          后序 尋找規(guī)則圖示

          搜索樹的中值

          搜索最大值和最小值

          使用肉眼可以直觀找到樹的最大值和最小值。最小值在最末端左側(cè),最大值在最末端右側(cè);

          查找最小值,遞歸遍歷left節(jié)點(diǎn),因?yàn)樽髠?cè)節(jié)點(diǎn)是最小的,遍歷到最下面一個(gè)就是最小的值然后返回;遍歷最大值同理,遍歷right 右側(cè)節(jié)點(diǎn)即可

              //  查找最小值    min(){        return this.minNode(this.root);    }    minNode(node){        while(node !== null && node.left !== null){            node = node.left;        }        return node.key;    }    //  查找最大值    max(){        return this.maxNode(this.root)    }    maxNode(node){        while(node !== null && node.right !== null){            node = node.right;        }        return node.key???????}
          ????}????

          搜索一個(gè)特定的值

          尋找一棵樹或其任意子樹中的一個(gè)特定的值。

            
            // 搜索一個(gè)特定的值    search(key) {        return this.searchNode(this.root, key)    }    searchNode(node, key) {        if (node === null) return false;              let result = this.defaultCompare(key, node.key)        if (result === -1) {            return this.searchNode(node.left, key);        } else if (result === 1) {            return this.searchNode(node.right, key)        } else {            return true;        }    }    console.log(b.search(1));   //  false    console.log(b.search(8));   //  true
          移除指定節(jié)點(diǎn)

          注意??:這里的移除節(jié)點(diǎn)返回的是移除完節(jié)點(diǎn)后的樹,而不是返回移除完的值(我在這里繞了半天,因?yàn)闆]有仔細(xì)看返回值~~)

            
           //  移除 節(jié)點(diǎn)返回一個(gè)新的root樹,而不是返回移除的節(jié)點(diǎn)值    remove(key) {        this.root = this.removeNode(this.root, key);    }    removeNode(node, key) {        if (node === null) return false;        //  key 比 當(dāng)前節(jié)點(diǎn)小        if (this.defaultCompare(key, node.key) === -1) {            node.left = this.removeNode(node.left, key)            return node;        } else if (this.defaultCompare(key, node.key) === 1) {            //  key 比當(dāng)前節(jié)點(diǎn)大            node.right = this.removeNode(node.right, key);            return node;        } else {            //  { 1 } 情況1: 移除沒有左右葉的節(jié)點(diǎn)            if (node.let === null && node.right === null) {                node = null;                return node;            }            // { 2 } 情況2:移除只存在 左葉或右葉的 節(jié)點(diǎn)            if (node.left === null) {                node = node.left;   //  null  賦值給當(dāng)前節(jié)點(diǎn)表示移除                return node;            } else if (node.right === null) {                node = node.right   //  null  賦值給當(dāng)前節(jié)點(diǎn)表示移除                return node;            }            // { 3 }  情況3:移除有兩個(gè)子節(jié)點(diǎn)的節(jié)點(diǎn)            const aux = this.minNode(node.right);   //  找到右側(cè)最小節(jié)點(diǎn)            node.key = aux.key; //  最小節(jié)點(diǎn)更新 要移除的這個(gè)節(jié)點(diǎn)            node.right = this.removeNode(node.right, aux.key);  //  移除掉原來位置的值            return node;        }    }
          移除沒有左右葉的節(jié)點(diǎn)

          見代碼 { 1 },判斷當(dāng)前節(jié)點(diǎn)的左葉或右葉是否都是null,如果是的話,把當(dāng)前節(jié)點(diǎn)賦值給null(移除當(dāng)前節(jié)點(diǎn));

          移除存有左側(cè)或右側(cè)節(jié)點(diǎn)的節(jié)點(diǎn)

          見代碼{ 2 },先判斷左側(cè)或右側(cè)節(jié)點(diǎn)是否為null,如果是的話,跳過這個(gè)節(jié)點(diǎn),直接將父節(jié)點(diǎn)指針指向子節(jié)點(diǎn)。(不存在的這個(gè)節(jié)點(diǎn)肯定是null,null 賦值給當(dāng)前節(jié)點(diǎn)表示刪除)

          移除的節(jié)點(diǎn)存在兩子節(jié)點(diǎn)

          見代碼{ 3 },要移除的這個(gè)節(jié)點(diǎn)下面有兩個(gè)節(jié)點(diǎn)。注??:只是移除存在兩個(gè)節(jié)點(diǎn)的這個(gè)節(jié)點(diǎn),不移除當(dāng)前節(jié)點(diǎn)下面的兩個(gè)子節(jié)點(diǎn);

          • 先找到右邊節(jié)點(diǎn)下的 最小的節(jié)點(diǎn) ,因?yàn)楦福娓腹?jié)點(diǎn)一定比右側(cè)小,左側(cè)大。所有要找到最小節(jié)點(diǎn);

          • 找到右側(cè)最小節(jié)點(diǎn)的值,去更新要移除的這個(gè)節(jié)點(diǎn)的值。也就是移除掉了要移除的值,把比左側(cè)大 右側(cè)小的值,推上了被替換掉的這個(gè)值的位置。

          • 此時(shí)存在兩個(gè)相同的節(jié)點(diǎn)(第一條這個(gè)值),第一個(gè)在原來節(jié)點(diǎn),第二個(gè)在替換掉的那個(gè)值的位置。所以要把它原來位置的值刪掉,保證樹中不存在兩個(gè)相同的值;

          • 返回樹

          完整代碼見 https://github.com/qdwds/data-structure/blob/master/src/binarySearchTree/binarySearchTree.js


          https://www.yuque.com/qdwds/uee630/qe52ca

          瀏覽 34
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲黄色成人视频网站 | 国产精品久久久久夜色 | 国产乱人乱偷精品视频a人人澡 | 欧美成人一级 | 五月午夜激情 |