国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

node.js|vue.js|jquery|angularjs|React|json|js教程|

服務器之家 - 編程語言 - JavaScript - js教程 - 如何利用JavaScript實現二叉搜索樹

如何利用JavaScript實現二叉搜索樹

2022-02-22 16:32瘋狂的技術宅 js教程

這篇文章主要給大家介紹了關于如何利用JavaScript實現二叉搜索樹的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

計算機科學中最常用和討論最多的數據結構之一是二叉搜索樹。這通常是引入的第一個具有非線性插入算法的數據結構。二叉搜索樹類似于雙鏈表,每個節點包含一些數據,以及兩個指向其他節點的指針;它們在這些節點彼此相關聯的方式上有所不同。二叉搜索樹節點的指針通常被稱為“左”和“右”,用來指示與當前值相關的子樹。這種節點的簡單 JavaScript 實現如下:

?
1
2
3
4
5
var node = {
  value: 125,
  left: null,
  right: null
};

從名稱中可以看出,二叉搜索樹被組織成分層的樹狀結構。第一個項目成為根節點,每個附加值作為該根的祖先添加到樹中。但是,二叉搜索樹節點上的值是唯一的,根據它們包含的值進行排序:作為節點左子樹的值總是小于節點的值,右子樹中的值都是大于節點的值。通過這種方式,在二叉搜索樹中查找值變得非常簡單,只要你要查找的值小于正在處理的節點則向左,如果值更大,則向右移動。二叉搜索樹中不能有重復項,因為重復會破壞這種關系。下圖表示一個簡單的二叉搜索樹。

如何利用JavaScript實現二叉搜索樹

上圖表示一個二叉搜索樹,其根的值為 8。當添加值 3 時,它成為根的左子節點,因為 3 小于 8。當添加值 1 時,它成為 3 的左子節點,因為 1 小于 8(所以向左)然后 1 小于3(再向左)。當添加值 10 時,它成為跟的右子節點,因為 10 大于 8。不斷用此過程繼續處理值 6,4,7,14 和 13。此二叉搜索樹的深度為 3,表示距離根最遠的節點是三個節點。

二叉搜索樹以自然排序的順序結束,因此可用于快速查找數據,因為你可以立即消除每個步驟的可能性。通過限制需要查找的節點數量,可以更快地進行搜索。假設你要在上面的樹中找到值 6。從根開始,確定 6 小于 8,因此前往根的左子節點。由于 6 大于 3,因此你將前往右側節點。你就能找到正確的值。所以你只需訪問三個而不是九個節點來查找這個值。

要在 JavaScript 中實現二叉搜索樹,第一步要先定義基本接口:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function BinarySearchTree() {
  this._root = null;
}
 
BinarySearchTree.prototype = {
 
  //restore constructor
  constructor: BinarySearchTree,
 
  add: function (value){
  },
 
  contains: function(value){
  },
 
  remove: function(value){
  },
 
  size: function(){
  },
 
  toArray: function(){
  },
 
  toString: function(){
  }
 
};

基本接與其他數據結構類似,有添加和刪除值的方法。我還添加了一些方便的方法,size(),toArray()和toString(),它們對 JavaScript 很有用。

要掌握使用二叉搜索樹的方法,最好從 contains() 方法開始。contains() 方法接受一個值作為參數,如果值存在于樹中則返回 true,否則返回 false。此方法遵循基本的二叉搜索算法來確定該值是否存在:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
BinarySearchTree.prototype = {
 
  //more code
 
  contains: function(value){
    var found    = false,
      current   = this._root
 
    //make sure there's a node to search
    while(!found && current){
 
      //if the value is less than the current node's, go left
      if (value < current.value){
        current = current.left;
 
      //if the value is greater than the current node's, go right
      } else if (value > current.value){
        current = current.right;
 
      //values are equal, found it!
      } else {
        found = true;
      }
    }
 
    //only proceed if the node was found
    return found;
  },
 
  //more code
 
};

搜索從樹的根開始。如果沒有添加數據,則可能沒有根,所以必須要進行檢查。遍歷樹遵循前面討論的簡單算法:如果要查找的值小于當前節點則向左移動,如果值更大則向右移動。每次都會覆蓋 current 指針,直到找到要找的值(在這種情況下 found 設置為 true)或者在那個方向上沒有更多的節點了(在這種情況下,值不在樹上)。

在 contains() 中使用的方法也可用于在樹中插入新值。主要區別在于你要尋找放置新值的位置,而不是在樹中查找值:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
BinarySearchTree.prototype = {
 
  //more code
 
  add: function(value){
    //create a new item object, place data in
    var node = {
        value: value,
        left: null,
        right: null
      },
 
      //used to traverse the structure
      current;
 
    //special case: no items in the tree yet
    if (this._root === null){
      this._root = node;
    } else {
      current = this._root;
 
      while(true){
 
        //if the new value is less than this node's value, go left
        if (value < current.value){
 
          //if there's no left, then the new node belongs there
          if (current.left === null){
            current.left = node;
            break;
          } else {
            current = current.left;
          }
 
        //if the new value is greater than this node's value, go right
        } else if (value > current.value){
 
          //if there's no right, then the new node belongs there
          if (current.right === null){
            current.right = node;
            break;
          } else {
            current = current.right;
          }  
 
        //if the new value is equal to the current one, just ignore
        } else {
          break;
        }
      }
    }
  },
 
  //more code
 
};

在二叉搜索樹中添加值時,特殊情況是在沒有根的情況。在這種情況下,只需將根設置為新值即可輕松完成工作。對于其他情況,基本算法與 contains() 中使用的基本算法完全相同:新值小于當前節點向左,如果值更大則向右。主要區別在于,當你無法繼續前進時,這就是新值的位置。所以如果你需要向左移動但沒有左側節點,則新值將成為左側節點(與右側節點相同)。由于不存在重復項,因此如果找到具有相同值的節點,則操作將停止。

在繼續討論 size() 方法之前,我想深入討論樹遍歷。為了計算二叉搜索樹的大小,必須要訪問樹中的每個節點。二叉搜索樹通常會有不同類型的遍歷方法,最常用的是有序遍歷。通過處理左子樹,然后是節點本身,然后是右子樹,在每個節點上執行有序遍歷。由于二叉搜索樹以這種方式排序,從左到右,結果是節點以正確的排序順序處理。對于 size() 方法,節點遍歷的順序實際上并不重要,但它對 toArray() 方法很重要。由于兩種方法都需要執行遍歷,我決定添加一個可以通用的 traverse() 方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
BinarySearchTree.prototype = {
 
  //more code
 
  traverse: function(process){
 
    //helper function
    function inOrder(node){
      if (node){
 
        //traverse the left subtree
        if (node.left !== null){
          inOrder(node.left);
        }     
 
        //call the process method on this node
        process.call(this, node);
 
        //traverse the right subtree
        if (node.right !== null){
          inOrder(node.right);
        }
      }
    }
 
    //start with the root
    inOrder(this._root);
  },
 
  //more code
 
};

此方法接受一個參數 process,這是一個應該在樹中的每個節點上運行的函數。該方法定義了一個名為 inOrder() 的輔助函數用于遞歸遍歷樹。注意,如果當前節點存在,則遞歸僅左右移動(以避免多次處理 null )。然后 traverse() 方法從根節點開始按順序遍歷,process() 函數處理每個節點。然后可以使用此方法實現size()、toArray()、toString():

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
BinarySearchTree.prototype = {
 
  //more code
 
  size: function(){
    var length = 0;
 
    this.traverse(function(node){
      length++;
    });
 
    return length;
  },
 
  toArray: function(){
    var result = [];
 
    this.traverse(function(node){
      result.push(node.value);
    });
 
    return result;
  },
 
  toString: function(){
    return this.toArray().toString();
  },
 
  //more code
 
};

size() 和 toArray() 都調用 traverse() 方法并傳入一個函數來在每個節點上運行。在使用 size()的情況下,函數只是遞增長度變量,而 toArray() 使用函數將節點的值添加到數組中。toString()方法在調用 toArray() 之前把返回的數組轉換為字符串,并返回  。

刪除節點時,你需要確定它是否為根節點。根節點的處理方式與其他節點類似,但明顯的例外是根節點需要在結尾處設置為不同的值。為簡單起見,這將被視為 JavaScript 代碼中的一個特例。

刪除節點的第一步是確定節點是否存在:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
BinarySearchTree.prototype = {
 
  //more code here
 
  remove: function(value){
 
    var found    = false,
      parent   = null,
      current   = this._root,
      childCount,
      replacement,
      replacementParent;
 
    //make sure there's a node to search
    while(!found && current){
 
      //if the value is less than the current node's, go left
      if (value < current.value){
        parent = current;
        current = current.left;
 
      //if the value is greater than the current node's, go right
      } else if (value > current.value){
        parent = current;
        current = current.right;
 
      //values are equal, found it!
      } else {
        found = true;
      }
    }
 
    //only proceed if the node was found
    if (found){
      //continue
    }
 
  },
  //more code here
 
};

remove()方法的第一部分是用二叉搜索定位要被刪除的節點,如果值小于當前節點的話則向左移動,如果值大于當前節點則向右移動。當遍歷時還會跟蹤 parent 節點,因為你最終需要從其父節點中刪除該節點。當 found 等于 true 時,current 的值是要刪除的節點。

刪除節點時需要注意三個條件:

  1. 葉子節點
  2. 只有一個孩子的節點
  3. 有兩個孩子的節點

從二叉搜索樹中刪除除了葉節點之外的內容意味著必須移動值來對樹正確的排序。前兩個實現起來相對簡單,只刪除了一個葉子節點,刪除了一個帶有一個子節點的節點并用其子節點替換。最后一種情況有點復雜,以便稍后訪問。

在了解如何刪除節點之前,你需要知道節點上究竟存在多少個子節點。一旦知道了,你必須確定節點是否為根節點,留下一個相當簡單的決策樹:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
BinarySearchTree.prototype = {
 
  //more code here
 
  remove: function(value){
 
    var found    = false,
      parent   = null,
      current   = this._root,
      childCount,
      replacement,
      replacementParent;
 
    //find the node (removed for space)
 
    //only proceed if the node was found
    if (found){
 
      //figure out how many children
      childCount = (current.left !== null ? 1 : 0) +
             (current.right !== null ? 1 : 0);
 
      //special case: the value is at the root
      if (current === this._root){
        switch(childCount){
 
          //no children, just erase the root
          case 0:
            this._root = null;
            break;
 
          //one child, use one as the root
          case 1:
            this._root = (current.right === null ?
                   current.left : current.right);
            break;
 
          //two children, little work to do
          case 2:
 
            //TODO
 
          //no default
 
        }   
 
      //non-root values
      } else {
 
        switch (childCount){
 
          //no children, just remove it from the parent
          case 0:
            //if the current value is less than its
            //parent's, null out the left pointer
            if (current.value < parent.value){
              parent.left = null;
 
            //if the current value is greater than its
            //parent's, null out the right pointer
            } else {
              parent.right = null;
            }
            break;
 
          //one child, just reassign to parent
          case 1:
            //if the current value is less than its
            //parent's, reset the left pointer
            if (current.value < parent.value){
              parent.left = (current.left === null ?
                      current.right : current.left);
 
            //if the current value is greater than its
            //parent's, reset the right pointer
            } else {
              parent.right = (current.left === null ?
                      current.right : current.left);
            }
            break
 
          //two children, a bit more complicated
          case 2:
 
            //TODO    
 
          //no default
 
        }
 
      }
 
    }
 
  },
 
  //more code here
 
};

處理根節點時,這是一個覆蓋它的簡單過程。對于非根節點,必須根據要刪除的節點的值設置 parent 上的相應指針:如果刪除的值小于父節點,則 left 指針必須重置為 null(對于沒有子節點的節點)或刪除節點的 left 指針;如果刪除的值大于父級,則必須將 right 指針重置為 null 或刪除的節點的 right指針。

如前所述,刪除具有兩個子節點的節點是最復雜的操作。下圖是兒茶搜索樹的一種表示。

如何利用JavaScript實現二叉搜索樹

根為 8,左子為 3,如果 3 被刪除會發生什么?有兩種可能性:1(3 左邊的孩子,稱為有序前身)或4(右子樹的最左邊的孩子,稱為有序繼承者)都可以取代 3。

這兩個選項中的任何一個都是合適的。要查找有序前驅,即刪除值之前的值,請檢查要刪除的節點的左子樹,并選擇最右側的子節點;找到有序后繼,在刪除值后立即出現的值,反轉進程并檢查最左側的右子樹。其中每個都需要另一次遍歷樹來完成操作:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
BinarySearchTree.prototype = {
 
  //more code here
 
  remove: function(value){
 
    var found    = false,
      parent   = null,
      current   = this._root,
      childCount,
      replacement,
      replacementParent;
 
    //find the node (removed for space)
 
    //only proceed if the node was found
    if (found){
 
      //figure out how many children
      childCount = (current.left !== null ? 1 : 0) +
             (current.right !== null ? 1 : 0);
 
      //special case: the value is at the root
      if (current === this._root){
        switch(childCount){
 
          //other cases removed to save space
 
          //two children, little work to do
          case 2:
 
            //new root will be the old root's left child
            //...maybe
            replacement = this._root.left;
 
            //find the right-most leaf node to be
            //the real new root
            while (replacement.right !== null){
              replacementParent = replacement;
              replacement = replacement.right;
            }
 
            //it's not the first node on the left
            if (replacementParent !== null){
 
              //remove the new root from it's
              //previous position
              replacementParent.right = replacement.left;
 
              //give the new root all of the old
              //root's children
              replacement.right = this._root.right;
              replacement.left = this._root.left;
            } else {
 
              //just assign the children
              replacement.right = this._root.right;
            }
 
            //officially assign new root
            this._root = replacement;
 
          //no default
 
        }   
 
      //non-root values
      } else {
 
        switch (childCount){
 
          //other cases removed to save space
 
          //two children, a bit more complicated
          case 2:
 
            //reset pointers for new traversal
            replacement = current.left;
            replacementParent = current;
 
            //find the right-most node
            while(replacement.right !== null){
              replacementParent = replacement;
              replacement = replacement.right;
            }
 
            replacementParent.right = replacement.left;
 
            //assign children to the replacement
            replacement.right = current.right;
            replacement.left = current.left;
 
            //place the replacement in the right spot
            if (current.value < parent.value){
              parent.left = replacement;
            } else {
              parent.right = replacement;
            }    
 
          //no default
 
        }
 
      }
 
    }
 
  },
 
  //more code here
 
};

具有兩個子節點的根節點和非根節點的代碼幾乎相同。此實現始終通過查看左子樹并查找最右側子節點來查找有序前驅。遍歷是使用 while 循環中的 replacement 和 replacementParent 變量完成的。replacement中的節點最終成為替換 current 的節點,因此通過將其父級的 right 指針設置為替換的 left 指針,將其從當前位置移除。對于根節點,當 replacement 是根節點的直接子節點時,replacementParent 將為 null,因此 replacement 的 right 指針只是設置為 root 的 right 指針。最后一步是將替換節點分配到正確的位置。對于根節點,替換設置為新根;對于非根節點,替換被分配到原始 parent 上的適當位置。

說明:始終用有序前驅替換節點可能導致不平衡樹,其中大多數值會位于樹的一側。不平衡樹意味著搜索效率較低,因此在實際場景中應該引起關注。在二叉搜索樹實現中,要確定是用有序前驅還是有序后繼以使樹保持適當平衡(通常稱為自平衡二叉搜索樹)。

總結

到此這篇關于如何利用JavaScript實現二叉搜索樹的文章就介紹到這了,更多相關JavaScript二叉搜索樹內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://mp.weixin.qq.com/s/lfXE64hA8zXGWr9NmYbMcg

延伸 · 閱讀

精彩推薦
  • js教程JavaScript實現消消樂的源代碼

    JavaScript實現消消樂的源代碼

    這篇文章主要介紹了JavaScript實現消消樂-源代碼,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以...

    代碼100分12202021-12-30
  • js教程使用Webpack構建多頁面程序的實現步驟

    使用Webpack構建多頁面程序的實現步驟

    這篇文章主要介紹了使用Webpack構建多頁面程序的實現步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋...

    Tuzilow9532022-02-17
  • js教程js動態生成表格(節點操作)

    js動態生成表格(節點操作)

    這篇文章主要為大家詳細介紹了js動態生成表格,進行節點操作,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    劉劉劉code3622021-12-30
  • js教程JavaScript實現篩選數組

    JavaScript實現篩選數組

    這篇文章主要為大家詳細介紹了JavaScript實現篩選數組,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    崇志廣勤7372022-01-25
  • js教程微信小程序實現簡單購物車功能

    微信小程序實現簡單購物車功能

    這篇文章主要為大家詳細介紹了微信小程序實現簡單購物車功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    mossbaoo5332021-12-22
  • js教程JavaScript實現二叉搜索樹

    JavaScript實現二叉搜索樹

    這篇文章主要為大家詳細介紹了JavaScript實現二叉搜索樹,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    希魔王的塔羅牌7232022-02-13
  • js教程three.js顯示中文字體與tween應用詳析

    three.js顯示中文字體與tween應用詳析

    這篇文章主要給大家介紹了關于three.js顯示中文字體與tween應用的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習...

    郭志強10002021-12-24
  • js教程js 創建對象的多種方式與優缺點小結

    js 創建對象的多種方式與優缺點小結

    這篇文章主要介紹了js 創建對象的多種方式與優缺點,幫助大家更好的理解和學習使用JavaScript,感興趣的朋友可以了解下...

    feng12052022-02-15
主站蜘蛛池模板: 木耳av在线| 玖玖玖视频 | 亚洲情av | 国产免费一区 | 国产精品国产 | 久久精品视频免费观看 | av亚洲在线 | 国产一区二区免费 | 毛片区 | 激情久久久久 | 日韩中文字幕视频 | 色官网 | 亚洲成人一区 | 国产精品黄色 | 高清国产视频 | 欧美精品国产精品 | av一区二区三区 | 国产精品一区二区三区四区 | 免费成人黄色大片 | 国产精品69毛片高清亚洲 | 日本中文在线视频 | 久久久久久亚洲精品视频 | 中文字幕精品一区 | 国产精品久久久久久久久久久久久 | 国产成人网 | 欧美成年网站 | 在线观看国产一区二区 | 成人高清视频在线观看 | 亚洲aⅴ天堂av在线电影软件 | 精品国产乱码久久久久久1区2区 | 亚洲高清免费视频 | 日韩国产一区二区三区 | 鲁一鲁综合 | 一二三区视频 | 久久的爱 | 日韩午夜在线 | 久久99这里只有精品 | 国产精品suv一区二区 | 免费看的av | 懂色av一区二区三区 | 五月天婷婷免费视频 |