自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

前端: JavaScript 中的二叉樹算法實現(xiàn)

開發(fā) 前端 算法
接下來讓我們一起來探討js數(shù)據(jù)結(jié)構(gòu)中的樹。這里的樹類比現(xiàn)實生活中的樹,有樹干,樹枝,在程序中樹是一種數(shù)據(jù)結(jié)構(gòu),對于存儲需要快速查找的數(shù)據(jù)非有用,它是一種分層數(shù)據(jù)的抽象模型。一個樹結(jié)構(gòu)包含一系列存在父子關系的節(jié)點。每個節(jié)點都有一個父節(jié)點以及零個或多個子節(jié)點。

[[359197]]

接下來讓我們一起來探討js數(shù)據(jù)結(jié)構(gòu)中的樹。這里的樹類比現(xiàn)實生活中的樹,有樹干,樹枝,在程序中樹是一種數(shù)據(jù)結(jié)構(gòu),對于存儲需要快速查找的數(shù)據(jù)非有用,它是一種分層數(shù)據(jù)的抽象模型。一個樹結(jié)構(gòu)包含一系列存在父子關系的節(jié)點。每個節(jié)點都有一個父節(jié)點以及零個或多個子節(jié)點。如下所以為一個樹結(jié)構(gòu):)


  • 和樹相關的概念:1.子樹:由節(jié)點和他的后代構(gòu)成,如上圖標示處。2.深度:節(jié)點的深度取決于它祖節(jié)點的數(shù)量,比如節(jié)點5有2個祖節(jié)點,他的深度為2。3.高度:樹的高度取決于所有節(jié)點深度的最大值。

二叉樹和二叉搜索樹介紹

二叉樹中的節(jié)點最多只能有2個子節(jié)點,一個是左側(cè)子節(jié)點,一個是右側(cè)子節(jié)點,這樣定義的好處是有利于我們寫出更高效的插入,查找,刪除節(jié)點的算法。二叉搜索樹是二叉樹的一種,但是它只允許你在左側(cè)子節(jié)點存儲比父節(jié)點小的值,但在右側(cè)節(jié)點存儲比父節(jié)點大的值。接下來我們將按照這個思路去實現(xiàn)一個二叉搜索樹。

1. 創(chuàng)建BinarySearchTree類

這里我們將使用構(gòu)造函數(shù)去創(chuàng)建一個類:

  1. function BinarySearchTree(){ 
  2.     // 用于創(chuàng)建節(jié)點的類 
  3.     let Node = function(key) { 
  4.         this.key = key
  5.         this.left = null
  6.         this.right = null
  7.     } 
  8.     // 根節(jié)點 
  9.     let root = null

我們將使用和鏈表類似的指針方式去表示節(jié)點之間的關系,如果不了解鏈表,請看我后序的文章《如何實現(xiàn)單向鏈表和雙向鏈表》。

2.插入一個鍵

  1. // 插入一個鍵 
  2. this.insert = function(key) { 
  3.     let newNode = new Node(key); 
  4.     root === null ? (root = newNode) : (insertNode(root, newNode)) 

向樹中插入一個新的節(jié)點主要有以下三部分:1.創(chuàng)建新節(jié)點的Node類實例 --> 2.判斷插入操作是否為根節(jié)點,是根節(jié)點就將其指向根節(jié)點 --> 3.將節(jié)點加入非根節(jié)點的其他位置。

insertNode的具體實現(xiàn)如下:

  1. function insertNode(node, newNode){ 
  2.     if(newNode.key < node.key) { 
  3.         node.left === null ? (node.left = newNode) : (insertNode(node.left, newNode)) 
  4.     }else { 
  5.         node.right === null ? (node.right = newNode) : (insertNode(node.right, newNode)) 
  6.     } 

這里我們用到遞歸,接下來要實現(xiàn)的search,del等都會大量使用遞歸,所以說不了解的可以先自行學習了解。我們創(chuàng)建一個二叉樹實例,來插入一個鍵:

  1. let tree = new BinarySearchTree(); 
  2. tree.insert(20); 
  3. tree.insert(21); 
  4. tree.insert(520); 
  5. tree.insert(521); 

插入的結(jié)構(gòu)會按照二叉搜索樹的規(guī)則去插入,結(jié)構(gòu)類似于上文的第一個樹圖。

樹的遍歷

訪問樹的所有節(jié)點有三種遍歷方式:中序,先序和后序。

  • 中序遍歷:以從最小到最大的順序訪問所有節(jié)點
  • 先序遍歷:以優(yōu)先于后代節(jié)點的順序訪問每個節(jié)點
  • 后序遍歷:先訪問節(jié)點的后代節(jié)點再訪問節(jié)點本身

根據(jù)以上的介紹,我們可以有以下的實現(xiàn)代碼。

1 中序排序

  1. this.inOrderTraverse = function(cb){ 
  2.     inOrderTraverseNode(root, cb); 
  3.  
  4. // 輔助函數(shù) 
  5. function inOrderTraverseNode(node, cb){ 
  6.     if(node !== null){ 
  7.         inOrderTraverseNode(node.left, cb); 
  8.         cb(node.key); 
  9.         inOrderTraverseNode(node.right, cb); 
  10.     } 

使用中序遍歷可以實現(xiàn)對樹進行從小到大排序的功能。

2 先序排序

  1. // 先序排序 --- 優(yōu)先于后代節(jié)點的順序訪問每個節(jié)點 
  2.    this.preOrderTraverse = function(cb) { 
  3.      preOrderTraverseNode(root, cb); 
  4.    } 
  5.  
  6.    // 先序排序輔助方法 
  7.    function preOrderTraverseNode(node, cb) { 
  8.      if(node !== null) { 
  9.        cb(node.key); 
  10.        preOrderTraverseNode(node.left, cb); 
  11.        preOrderTraverseNode(node.right, cb); 
  12.      } 
  13.    } 

使用先序排序可以實現(xiàn)結(jié)構(gòu)化輸出的功能。

3 后序排序

  1. // 后續(xù)遍歷 --- 先訪問后代節(jié)點,再訪問節(jié)點本身 
  2.    this.postOrderTraverse = function(cb) { 
  3.      postOrderTraverseNode(root, cb); 
  4.    } 
  5.  
  6.    // 后續(xù)遍歷輔助方法 
  7.    function postOrderTraverseNode(node, cb) { 
  8.      if(node !== null){ 
  9.        postOrderTraverseNode(node.left, cb); 
  10.        postOrderTraverseNode(node.right, cb); 
  11.        cb(node.key); 
  12.      } 
  13.    } 

后序遍歷可以用于計算有層級關系的所有元素的大小。

搜索樹中的值

在樹中有三種經(jīng)常執(zhí)行的搜索類型:最大值,最小值,特定的值。

1 最小值

最小值通過定義可以知道即是左側(cè)樹的最底端的節(jié)點,具體實現(xiàn)代碼如下:

  1. // 最小值 
  2.    this.min = function(){ 
  3.      return minNode(root) 
  4.    } 
  5.  
  6.     function minNode(node) { 
  7.      if(node) { 
  8.        while(node && node.left !== null){ 
  9.          node = node.left
  10.        } 
  11.        return node.key 
  12.      } 
  13.      return null 
  14.    } 

相似的,實現(xiàn)最大值的方法如下:

  1. // 最大值 
  2.    this.max = function() { 
  3.      return maxNode(root) 
  4.    } 
  5.  
  6.    function maxNode(node) { 
  7.      if(node){ 
  8.        while(node && node.right !== null){ 
  9.          node = node.right
  10.        } 
  11.        return node.key 
  12.      } 
  13.      return null 
  14.    } 

2.搜索一個特定的值

  1. // 搜索樹中某個值 
  2. this.search = function(key) { 
  3.     return searchNode(root, key
  4.  
  5. // 搜索輔助方法 
  6. function searchNode(node, key){ 
  7.     if(node === null) { 
  8.         return false 
  9.     } 
  10.     if(key < node.key) { 
  11.         return searchNode(node.leftkey
  12.     } else if(key > node.key) { 
  13.         return searchNode(node.rightkey
  14.     }else { 
  15.         return true 
  16.     } 

3 移除一個節(jié)點

  1. this.remove = function(key){ 
  2.     root = removeNode(root, key); 
  3.  
  4. // 發(fā)現(xiàn)最小節(jié)點 
  5. function findMinNode(node) { 
  6.     if(node) { 
  7.     while(node && node.left !== null){ 
  8.         node = node.left
  9.     } 
  10.     return node 
  11.     } 
  12.     return null 
  13.  
  14. // 移除節(jié)點輔助方法 
  15. function removeNode(node, key) { 
  16.     if(node === null) { 
  17.     return null 
  18.     } 
  19.  
  20.     if(key < node.key){ 
  21.     node.left = removeNode(node.leftkey); 
  22.     return node 
  23.     } else if( key > node.key){ 
  24.     node.right = removeNode(node.rightkey); 
  25.     return node 
  26.     } else { 
  27.     // 一個頁節(jié)點 
  28.     if(node.left === null && node.right === null) { 
  29.         node = null
  30.         return node 
  31.     } 
  32.  
  33.     // 只有一個子節(jié)點的節(jié)點 
  34.     if(node.left === null) { 
  35.         node = node.right
  36.         return node 
  37.     }else if(node.right === null) { 
  38.         node = node.left
  39.         return node 
  40.     } 
  41.  
  42.     // 有兩個子節(jié)點的節(jié)點 
  43.     let aux = findMinNode(node.right); 
  44.     node.key = aux.key
  45.     node.right = removeNode(node.right, aux.key); 
  46.     return node 
  47.     } 

刪除節(jié)點需要考慮的情況比較多,這里我們會使用和min類似的實現(xiàn)去寫一個發(fā)現(xiàn)最小節(jié)點的函數(shù),當要刪除的節(jié)點有兩個子節(jié)點時,我們要將當前要刪除的節(jié)點替換為子節(jié)點中最大的一個節(jié)點的值,然后將這個子節(jié)點刪除。

至此,一個二叉搜索樹已經(jīng)實現(xiàn),但是還存在一個問題,如果樹的一遍非常深,將會存在一定的性能問題,為了解決這個問題,我們可以利用AVL樹,一種自平衡二叉樹,也就是說任何一個節(jié)點的左右兩側(cè)子樹的高度之差最多為1。

 

責任編輯:姜華 來源: 趣談前端
相關推薦

2020-04-27 07:05:58

二叉樹左子樹右子樹

2021-09-29 10:19:00

算法平衡二叉樹

2013-07-15 16:35:55

二叉樹迭代器

2020-09-23 18:25:40

算法二叉樹多叉樹

2021-04-20 08:37:14

數(shù)據(jù)結(jié)構(gòu)二叉樹

2009-08-11 13:29:57

C#二叉樹遍歷

2021-09-15 07:56:32

二叉樹層次遍歷

2021-09-28 06:28:51

二叉樹公共祖先

2021-04-19 07:47:42

數(shù)據(jù)結(jié)構(gòu)二叉樹Tree

2021-04-28 20:12:27

數(shù)據(jù)結(jié)構(gòu)創(chuàng)建

2020-12-30 08:35:34

貪心算法監(jiān)控

2021-08-27 11:36:44

二叉樹回溯節(jié)點

2022-10-26 23:58:02

二叉樹數(shù)組算法

2021-03-17 08:19:22

二叉樹LeetCode

2020-11-02 09:15:47

算法與數(shù)據(jù)結(jié)構(gòu)

2021-10-12 09:25:11

二叉樹樹形結(jié)構(gòu)

2018-03-15 08:31:57

二叉樹存儲結(jié)構(gòu)

2021-03-22 08:23:29

LeetCode二叉樹節(jié)點

2023-05-08 15:57:16

二叉樹數(shù)據(jù)結(jié)構(gòu)

2021-05-06 17:46:30

二叉樹數(shù)據(jù)結(jié)構(gòu)
點贊
收藏

51CTO技術(shù)棧公眾號