二叉樹的節點最多只能有兩個子節點,一個左側子節點,一個右側子節點。
二叉搜索樹(BST),是二叉樹的一種,但只允許在左側節點存儲比父節點小的值,在右側節點存儲比父節點大或等於父節點的值。
1.創建BST
1.1創建BST類
首先申明BST類的基本結構
function BinarySearchTree() { var Node = function(key){ this.key = key; this.left = null; this.right = null; }; var root = null; }
下面我們實現一些基本方法
1.2 向樹中插入一個鍵
插入一個鍵時,有三步:第一步,創建一個表示新節點的Node類實例;第二步,判斷是否有根節點,如果沒有,則插入節點為根節點;第三步,如果已經有根節點,需要一個輔助方法來判斷新節點的
插入位置,方法如下
var insertNode = function(node,newNode){ 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); } } }
以上方法的意思是,先用新節點和根節點作對比,判斷根節點左側(新節點小於根節點)或右側(新節點大於根節點)子節點是否有值,如果沒有則插入,如果有,則繼續用新節點和根節點的子節點比
大小,做遞歸操作,逐步得到新節點插入位置,下面是插入節點的方法
this.insert = function(key){ var newNode = new Node(key); if (root === null) { root = newNode; } else{ insertNode(node.left, newNode); } }
1.3 遍歷樹
遍歷一棵樹是指訪問樹的每一個節點,並對它們進行某種操作的過程。訪問樹的節點有三種方法,中序,先序和后序。
1.3.1 中序遍歷
以最小到最大的順序訪問所有的節點
var inOrderTraverseNode = function(node,callback){ if (node !== null) { inOrderTraverseNode(node.left,callback); callback(node.key); inOrderTraverseNode(node.right,callback); } } this.inOrderTraverse = function(callback){ inOrderTraverseNode(root,callback); }
以上代碼中,inOrderTraverse方法接受一個回掉函數作為參數,來定義對遍歷到的每一個節點的操作,這個方法中調用了一個輔助方法inOrderTraverseNode,用來遞歸遍歷所有值,當遞歸到最后一級
時,觀察輔助函數內部,調用回掉函數依次是左側子節點,父節點,右側子節點,所以執行回調函數順序為按照節點值從小到大。
1.3.2 先序遍歷
祖先節點優先於后代節點的順序遍歷
var preOrderTraverseNode = function(node,callback){ if (node !== null) { callback(node.key); preOrderTraverseNode(node.left,callback); preOrderTraverseNode(node.right,callback); } }; this.preOrderTraverse = function(callback){ preOrderTraverseNode(root,callback); }
以上代碼中,preOrderTraverse方法接受一個回掉函數作為參數,來定義對遍歷到的每一個節點的操作,這個方法中調用了一個輔助方法preOrderTraverseNode,用來遞歸遍歷所有值,當遞歸到最后一級
時,觀察輔助函數內部,調用回掉函數依次是父節點,左側子節點,右側子節點,所以遍歷優先祖先節點。
1.3.3 后序遍歷
先訪問節點的后代節點,在訪問節點本身
var postOrderTraverseNode = function(node,callback){ if (node !== null) { callback(node.key); postOrderTraverseNode(node.left,callback); postOrderTraverseNode(node.right,callback); } }; this.postOrderTraverse = function(callback){ postOrderTraverseNode(root,callback); }
以上代碼中,postOrderTraverse方法接受一個回掉函數作為參數,來定義對遍歷到的每一個節點的操作,這個方法中調用了一個輔助方法postOrderTraverseNode,用來遞歸遍歷所有值,當遞歸到最后一級
時,觀察輔助函數內部,調用回掉函數依次是左側子節點,右側子節點,父節點,所以遍歷優先子節點。
1.4 搜索樹中的值
1.4.1 最大值最小值
最大值為搜索樹右下角的值,最小值為搜索樹左下角的值,下面來看實現
1.最大值
var maxNode = function(node){ if (node) { while(node&&node.right !== null){ node = node.right; } return node.key; } return null; }; this.max = function(){ return maxNode(root); }
2.最小值
var minNode = function(node){ if (node) { while(node&&node.left !== null){ node = node.left; } return node.key; } return null; }; this.min = function(){ return minNode(root); }
實現最大值最小值,同樣的方法,從根節點開始循環,直到右下角或左下角的值,返回回來。
1.4.2 搜索任意值
var searchNode = function(node,key){ if (node === null) { return false; } if (key<node.key) { return searchNode(node.left,key) } else if (key>node.key) { return searchNode(node.right,key) } else{ return true; } }; this.search = function(key){ return searchNode(root,key); }
邏輯其實很簡單,拿給定的節點從根節點開始查找,比根節點大,則繼續與根節點右側子節點比較,比根節點大,則繼續與根節點左側子節點比較,依次遞歸,直到給定節點與某節點相等,表示找到,
返回true,如果沒找到,返回false
1.4.3 刪除一個節點
var removeNode = function(node,key){ if (node === null) { return null; } if (key<node.key) { node.left = remove(node.left,key) } else if (key>node.key) { node.left = remove(node.right,key) } // 鍵等於node.key else{ //葉節點 if (node.left === null&&node.right === null) { node = null; return node; } // 只有一個子節點的節點 if (node.left === null) { node = node.right; return node; } else if(node.right === null) { node = node.left; return node; } // 有兩個子節點的節點 var aux = findMinNode(node.right); node.key = aux.key; node.right = removeNode(node.right,aux.key); return node; } } this.remove =function(key){ root = removeNode(root,key); }
上面當刪除節點有兩個子節點時,需要調用輔助方法findMinNode
var minNodeSelf = function(node){ if (node) { while(node&&node.left !== null){ node = node.left; } return node; } return null; }; this.findMinNode = function(){ return minNodeSelf(node); },
下面是實現BST的完整代碼
function BinarySearchTree() { var Node = function(key){ this.key = key; this.left = null; this.right = null; }; var root = null; var insertNode = function(node,newNode){ 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 inOrderTraverseNode = function(node,callback){ if (node !== null) { inOrderTraverseNode(node.left,callback); callback(node.key); inOrderTraverseNode(node.right,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) { callback(node.key); postOrderTraverseNode(node.left,callback); postOrderTraverseNode(node.right,callback); } }; var maxNode = function(node){ if (node) { while(node&&node.right !== null){ node = node.right; } return node.key; } return null; }; var minNode = function(node){ if (node) { while(node&&node.left !== null){ node = node.left; } return node.key; } return null; }; var minNodeSelf = function(node){ if (node) { while(node&&node.left !== null){ node = node.left; } return node; } return null; }; var searchNode = function(node,key){ if (node === null) { return false; } if (key<node.key) { return searchNode(node.left,key) } else if (key>node.key) { return searchNode(node.right,key) } else{ return true; } }; var removeNode = function(node,key){ if (node === null) { return null; } if (key<node.key) { node.left = remove(node.left,key) } else if (key>node.key) { node.left = remove(node.right,key) } // 鍵等於node.key else{ //葉節點 if (node.left === null&&node.right === null) { node = null; return node; } // 只有一個子節點的節點 if (node.left === null) { node = node.right; return node; } else if(node.right === null) { node = node.left; return node; } // 有兩個子節點的節點 var aux = findMinNode(node.right); node.key = aux.key; node.right = removeNode(node.right,aux.key); return node; } }; this.insert = function(key){ var newNode = new Node(key); if (root === null) { root = newNode; } else{ insertNode(node.left, newNode); } }, this.inOrderTraverse = function(callback){ inOrderTraverseNode(root,callback); }, this.preOrderTraverse = function(callback){ preOrderTraverseNode(root,callback); }, this.postOrderTraverse = function(callback){ postOrderTraverseNode(root,callback); }, this.max = function(){ return maxNode(root); }, this.min = function(){ return minNode(root); }, this.findMinNode = function(){ return minNodeSelf(root); }, this.search = function(key){ return searchNode(root,key); }, this.remove =function(key){ root = removeNode(root,key); } }